Commit ·
9c5d0ee
1
Parent(s): ce9045e
ân - script tính số Parameters
Browse files- count_params_act.py +154 -0
- count_params_dp.py +215 -0
count_params_act.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Đếm số tham số (Parameters) trực tiếp từ file model.safetensors của ACT
|
| 4 |
+
(Action Chunking Transformer).
|
| 5 |
+
|
| 6 |
+
Giống count_params_from_file.py (dùng cho Diffusion Policy): chỉ đọc header
|
| 7 |
+
JSON (vài KB đầu file) — KHÔNG cần load toàn bộ model vào RAM/VRAM, KHÔNG cần
|
| 8 |
+
cài PyTorch.
|
| 9 |
+
|
| 10 |
+
Thêm so với bản gốc: phân nhóm tham số theo module (backbone thị giác /
|
| 11 |
+
attention / feed-forward / embeddings / ...) — đặc thù cho kiến trúc ACT,
|
| 12 |
+
để biết phần nào đang chiếm dung lượng khi so sánh các checkpoint (vd. v2 vs v7).
|
| 13 |
+
|
| 14 |
+
Cách dùng:
|
| 15 |
+
python3 count_params_act.py /duong/dan/toi/model.safetensors
|
| 16 |
+
python3 count_params_act.py model_v2.safetensors model_v7.safetensors # so sánh nhiều file
|
| 17 |
+
"""
|
| 18 |
+
import json
|
| 19 |
+
import struct
|
| 20 |
+
import sys
|
| 21 |
+
from math import prod
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Thứ tự nhóm cũng là thứ tự hiển thị trong bảng kết quả.
|
| 25 |
+
# Mỗi rule là (tên_nhóm, hàm_kiểm_tra(tên_tensor) -> bool). Rule ở trên được
|
| 26 |
+
# ưu tiên trước — vd. "vae_encoder.layers" phải khớp trước "vae_encoder" chung chung.
|
| 27 |
+
GROUP_RULES = [
|
| 28 |
+
("Backbone thị giác (ResNet18)", lambda k: "backbone" in k),
|
| 29 |
+
("VAE encoder — các tầng Transformer", lambda k: k.startswith("model.vae_encoder.layers")),
|
| 30 |
+
("VAE encoder — projections/embeddings", lambda k: k.startswith("model.vae_encoder") and "layers" not in k),
|
| 31 |
+
("Transformer encoder — các tầng", lambda k: k.startswith("model.encoder.layers")),
|
| 32 |
+
("Transformer encoder — projections/embeddings", lambda k: k.startswith("model.encoder") and "layers" not in k),
|
| 33 |
+
("Transformer decoder — các tầng", lambda k: k.startswith("model.decoder.layers")),
|
| 34 |
+
("Transformer decoder — projections/embeddings", lambda k: k.startswith("model.decoder") and "layers" not in k),
|
| 35 |
+
("Action head (đầu ra hành động)", lambda k: "action_head" in k),
|
| 36 |
+
]
|
| 37 |
+
GROUP_OTHER = "Khác (chưa phân loại)"
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def group_of(tensor_name: str) -> str:
|
| 41 |
+
for group_name, match in GROUP_RULES:
|
| 42 |
+
if match(tensor_name):
|
| 43 |
+
return group_name
|
| 44 |
+
return GROUP_OTHER
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def load_header(path: str) -> dict:
|
| 48 |
+
with open(path, "rb") as f:
|
| 49 |
+
# 8 byte đầu = độ dài (uint64, little-endian) của phần header JSON
|
| 50 |
+
header_len = struct.unpack("<Q", f.read(8))[0]
|
| 51 |
+
header = json.loads(f.read(header_len))
|
| 52 |
+
# "__metadata__" không phải tensor, phải loại ra trước khi đếm
|
| 53 |
+
header.pop("__metadata__", None)
|
| 54 |
+
return header
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def count_params(path: str) -> dict:
|
| 58 |
+
header = load_header(path)
|
| 59 |
+
|
| 60 |
+
total_params = 0
|
| 61 |
+
total_bytes = 0
|
| 62 |
+
dtype_count: dict = {}
|
| 63 |
+
group_params: dict = {}
|
| 64 |
+
group_tensor_count: dict = {}
|
| 65 |
+
|
| 66 |
+
for name, info in header.items():
|
| 67 |
+
shape = info["shape"]
|
| 68 |
+
n_elem = prod(shape) if shape else 1 # tensor 0-chiều (scalar) vẫn tính là 1
|
| 69 |
+
total_params += n_elem
|
| 70 |
+
|
| 71 |
+
start, end = info["data_offsets"]
|
| 72 |
+
total_bytes += end - start
|
| 73 |
+
|
| 74 |
+
dt = info["dtype"]
|
| 75 |
+
dtype_count[dt] = dtype_count.get(dt, 0) + 1
|
| 76 |
+
|
| 77 |
+
g = group_of(name)
|
| 78 |
+
group_params[g] = group_params.get(g, 0) + n_elem
|
| 79 |
+
group_tensor_count[g] = group_tensor_count.get(g, 0) + 1
|
| 80 |
+
|
| 81 |
+
return {
|
| 82 |
+
"path": path,
|
| 83 |
+
"n_tensors": len(header),
|
| 84 |
+
"dtype_count": dtype_count,
|
| 85 |
+
"total_params": total_params,
|
| 86 |
+
"total_bytes": total_bytes,
|
| 87 |
+
"group_params": group_params,
|
| 88 |
+
"group_tensor_count": group_tensor_count,
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def print_report(r: dict) -> None:
|
| 93 |
+
print(f"File: {r['path']}")
|
| 94 |
+
print(f"Số tensor: {r['n_tensors']}")
|
| 95 |
+
print(f"Kiểu dữ liệu: {r['dtype_count']}")
|
| 96 |
+
print(f"Tổng tham số: {r['total_params']:,}")
|
| 97 |
+
print(f"Kích thước data: {r['total_bytes']:,} byte ({r['total_bytes']/1024**2:.1f} MiB)")
|
| 98 |
+
print()
|
| 99 |
+
print("--- Phân theo module ---")
|
| 100 |
+
header = f"{'Module':46s}{'#tensor':>9s}{'Tham số':>14s}{'%':>7s}"
|
| 101 |
+
print(header)
|
| 102 |
+
print("-" * len(header))
|
| 103 |
+
ordered = [g for g, _ in GROUP_RULES] + [GROUP_OTHER]
|
| 104 |
+
for g in ordered:
|
| 105 |
+
p = r["group_params"].get(g, 0)
|
| 106 |
+
if p == 0:
|
| 107 |
+
continue
|
| 108 |
+
n = r["group_tensor_count"].get(g, 0)
|
| 109 |
+
pct = 100 * p / r["total_params"] if r["total_params"] else 0
|
| 110 |
+
print(f"{g:46s}{n:9d}{p:14,d}{pct:6.1f}%")
|
| 111 |
+
print()
|
| 112 |
+
|
| 113 |
+
# Ước tính VRAM lúc train (FP32): trọng số + gradient + 2 hệ số Adam = 4x.
|
| 114 |
+
# Đây chỉ là phần do THAM SỐ đóng góp — activations (phụ thuộc batch_size
|
| 115 |
+
# và số camera) thường lớn hơn nhiều và KHÔNG được tính ở đây.
|
| 116 |
+
w_mib = r["total_params"] * 4 / 1024**2
|
| 117 |
+
print("--- Ước tính VRAM do THAM SỐ đóng góp (FP32, chưa tính activations) ---")
|
| 118 |
+
print(f"Trọng số: {w_mib:8.1f} MiB")
|
| 119 |
+
print(f"Inference (chỉ trọng số): {w_mib:8.1f} MiB")
|
| 120 |
+
print(f"Training (+grad +2x Adam): {4*w_mib:8.1f} MiB (~{4*w_mib/1024:.2f} GiB)")
|
| 121 |
+
print("Lưu ý: activations (ảnh camera) thường lớn hơn khoản này nhiều lần khi train.")
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def print_comparison(reports: list) -> None:
|
| 125 |
+
print("=== So sánh nhiều file ===")
|
| 126 |
+
header = f"{'File':30s}{'Tổng tham số':>16s}{'MiB':>10s}"
|
| 127 |
+
print(header)
|
| 128 |
+
print("-" * len(header))
|
| 129 |
+
for r in reports:
|
| 130 |
+
name = r["path"].split("/")[-1]
|
| 131 |
+
print(f"{name:30s}{r['total_params']:16,d}{r['total_bytes']/1024**2:10.1f}")
|
| 132 |
+
if len(reports) == 2:
|
| 133 |
+
a, b = reports
|
| 134 |
+
if b["total_params"]:
|
| 135 |
+
print(f"\nTỉ lệ {a['path'].split('/')[-1]} / {b['path'].split('/')[-1]}: "
|
| 136 |
+
f"{a['total_params']/b['total_params']:.3f}x")
|
| 137 |
+
print()
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
if __name__ == "__main__":
|
| 141 |
+
if len(sys.argv) < 2:
|
| 142 |
+
print("Dùng: python3 count_params_act.py <model1.safetensors> [model2.safetensors ...]")
|
| 143 |
+
sys.exit(1)
|
| 144 |
+
|
| 145 |
+
all_reports = []
|
| 146 |
+
for p in sys.argv[1:]:
|
| 147 |
+
rpt = count_params(p)
|
| 148 |
+
all_reports.append(rpt)
|
| 149 |
+
print_report(rpt)
|
| 150 |
+
print("=" * 70)
|
| 151 |
+
print()
|
| 152 |
+
|
| 153 |
+
if len(all_reports) > 1:
|
| 154 |
+
print_comparison(all_reports)
|
count_params_dp.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Đếm số tham số (Parameters) trực tiếp từ file model.safetensors của
|
| 4 |
+
Diffusion Policy (LeRobot).
|
| 5 |
+
|
| 6 |
+
Chỉ đọc header JSON (vài KB đầu file) — KHÔNG cần load toàn bộ model vào
|
| 7 |
+
RAM/VRAM, KHÔNG cần cài PyTorch. An toàn với file nhiều GB.
|
| 8 |
+
|
| 9 |
+
Thêm so với bản gốc (đồng bộ với count_params_act.py):
|
| 10 |
+
- Phân nhóm tham số theo module (vision encoder / U-Net down-mid-up / FiLM ...)
|
| 11 |
+
- Ước tính VRAM do THAM SỐ đóng góp (inference và training)
|
| 12 |
+
- Tách riêng tham số học được và buffer (BatchNorm running stats) — vì
|
| 13 |
+
safetensors lưu cả hai, nên "tổng phần tử" > "số tham số thực"
|
| 14 |
+
|
| 15 |
+
Cách dùng:
|
| 16 |
+
python3 count_params_from_file.py /duong/dan/toi/model.safetensors
|
| 17 |
+
python3 count_params_from_file.py dp_v1.safetensors dp_v2.safetensors # so sánh
|
| 18 |
+
"""
|
| 19 |
+
import json
|
| 20 |
+
import struct
|
| 21 |
+
import sys
|
| 22 |
+
from math import prod
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
# Số byte mỗi phần tử theo dtype của safetensors — dùng để ước tính VRAM
|
| 26 |
+
# cho đúng độ chính xác mà model đang được lưu (FP32 / FP16 / BF16).
|
| 27 |
+
DTYPE_BYTES = {
|
| 28 |
+
"F64": 8, "F32": 4, "F16": 2, "BF16": 2,
|
| 29 |
+
"I64": 8, "I32": 4, "I16": 2, "I8": 1, "U8": 1, "BOOL": 1,
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
# Thứ tự nhóm cũng là thứ tự hiển thị trong bảng kết quả.
|
| 33 |
+
# Mỗi rule là (tên_nhóm, hàm_kiểm_tra(tên_tensor) -> bool). Rule ở trên được
|
| 34 |
+
# ưu tiên trước — vd. "rgb_encoder ... backbone" phải khớp trước "rgb_encoder" chung.
|
| 35 |
+
GROUP_RULES = [
|
| 36 |
+
("Vision encoder — backbone ResNet-18", lambda k: "rgb_encoder" in k and "backbone" in k),
|
| 37 |
+
("Vision encoder — SpatialSoftmax + projection", lambda k: "rgb_encoder" in k),
|
| 38 |
+
("U-Net — diffusion_step_encoder", lambda k: "diffusion_step_encoder" in k),
|
| 39 |
+
("U-Net — down_modules (nhánh nén)", lambda k: "down_modules" in k),
|
| 40 |
+
("U-Net — mid_modules (bottleneck)", lambda k: "mid_modules" in k),
|
| 41 |
+
("U-Net — up_modules (nhánh giãn)", lambda k: "up_modules" in k),
|
| 42 |
+
("U-Net — final_conv (đầu ra action)", lambda k: "final_conv" in k),
|
| 43 |
+
("Normalization buffers (mean/std/min/max)", lambda k: "normalize" in k.lower()),
|
| 44 |
+
]
|
| 45 |
+
GROUP_OTHER = "Khác (chưa phân loại)"
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# Nhóm cắt ngang: FiLM conditioning nằm rải trong mọi residual block của U-Net,
|
| 49 |
+
# nên không thể tách bằng GROUP_RULES (sẽ trùng với down/mid/up). Thống kê riêng.
|
| 50 |
+
def is_film(name: str) -> bool:
|
| 51 |
+
return "cond_encoder" in name
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def is_buffer(name: str, dtype: str) -> bool:
|
| 55 |
+
"""Buffer = tensor được lưu trong file nhưng KHÔNG phải tham số học được."""
|
| 56 |
+
return (
|
| 57 |
+
"running_mean" in name
|
| 58 |
+
or "running_var" in name
|
| 59 |
+
or "num_batches_tracked" in name
|
| 60 |
+
or dtype in ("I64", "I32", "BOOL")
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def group_of(tensor_name: str) -> str:
|
| 65 |
+
for group_name, match in GROUP_RULES:
|
| 66 |
+
if match(tensor_name):
|
| 67 |
+
return group_name
|
| 68 |
+
return GROUP_OTHER
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def load_header(path: str) -> dict:
|
| 72 |
+
with open(path, "rb") as f:
|
| 73 |
+
# 8 byte đầu = độ dài (uint64, little-endian) của phần header JSON
|
| 74 |
+
header_len = struct.unpack("<Q", f.read(8))[0]
|
| 75 |
+
header = json.loads(f.read(header_len))
|
| 76 |
+
# "__metadata__" không phải tensor, phải loại ra trước khi đếm
|
| 77 |
+
header.pop("__metadata__", None)
|
| 78 |
+
return header
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def count_params(path: str) -> dict:
|
| 82 |
+
header = load_header(path)
|
| 83 |
+
|
| 84 |
+
total_elems = 0 # mọi phần tử được lưu trong file
|
| 85 |
+
learnable = 0 # tham số học được (đã loại buffer)
|
| 86 |
+
buffers = 0 # BN running stats, num_batches_tracked, ...
|
| 87 |
+
total_bytes = 0
|
| 88 |
+
film_params = 0
|
| 89 |
+
dtype_count: dict = {}
|
| 90 |
+
group_params: dict = {}
|
| 91 |
+
group_tensor_count: dict = {}
|
| 92 |
+
|
| 93 |
+
for name, info in header.items():
|
| 94 |
+
shape = info["shape"]
|
| 95 |
+
n_elem = prod(shape) if shape else 1 # tensor 0-chiều (scalar) vẫn tính là 1
|
| 96 |
+
dt = info["dtype"]
|
| 97 |
+
|
| 98 |
+
total_elems += n_elem
|
| 99 |
+
if is_buffer(name, dt):
|
| 100 |
+
buffers += n_elem
|
| 101 |
+
else:
|
| 102 |
+
learnable += n_elem
|
| 103 |
+
if is_film(name):
|
| 104 |
+
film_params += n_elem
|
| 105 |
+
|
| 106 |
+
start, end = info["data_offsets"]
|
| 107 |
+
total_bytes += end - start
|
| 108 |
+
|
| 109 |
+
dtype_count[dt] = dtype_count.get(dt, 0) + 1
|
| 110 |
+
|
| 111 |
+
g = group_of(name)
|
| 112 |
+
group_params[g] = group_params.get(g, 0) + n_elem
|
| 113 |
+
group_tensor_count[g] = group_tensor_count.get(g, 0) + 1
|
| 114 |
+
|
| 115 |
+
# dtype chiếm đa số — dùng để ước tính VRAM đúng với độ chính xác đang lưu
|
| 116 |
+
dominant_dtype = max(dtype_count, key=dtype_count.get) if dtype_count else "F32"
|
| 117 |
+
bytes_per_elem = DTYPE_BYTES.get(dominant_dtype, 4)
|
| 118 |
+
|
| 119 |
+
return {
|
| 120 |
+
"path": path,
|
| 121 |
+
"n_tensors": len(header),
|
| 122 |
+
"dtype_count": dtype_count,
|
| 123 |
+
"dominant_dtype": dominant_dtype,
|
| 124 |
+
"bytes_per_elem": bytes_per_elem,
|
| 125 |
+
"total_elems": total_elems,
|
| 126 |
+
"learnable": learnable,
|
| 127 |
+
"buffers": buffers,
|
| 128 |
+
"film_params": film_params,
|
| 129 |
+
"total_bytes": total_bytes,
|
| 130 |
+
"group_params": group_params,
|
| 131 |
+
"group_tensor_count": group_tensor_count,
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def print_report(r: dict) -> None:
|
| 136 |
+
print(f"File: {r['path']}")
|
| 137 |
+
print(f"Số tensor: {r['n_tensors']}")
|
| 138 |
+
print(f"Kiểu dữ liệu: {r['dtype_count']}")
|
| 139 |
+
print(f"Tổng phần tử: {r['total_elems']:,}")
|
| 140 |
+
print(f" ├─ tham số học được: {r['learnable']:,}")
|
| 141 |
+
print(f" └─ buffer (BN stats): {r['buffers']:,}")
|
| 142 |
+
print(f"Kích thước data: {r['total_bytes']:,} byte "
|
| 143 |
+
f"({r['total_bytes']/1024**2:.1f} MiB / {r['total_bytes']/1e9:.3f} GB)")
|
| 144 |
+
print()
|
| 145 |
+
|
| 146 |
+
print("--- Phân theo module ---")
|
| 147 |
+
head = f"{'Module':46s}{'#tensor':>9s}{'Tham số':>14s}{'%':>7s}"
|
| 148 |
+
print(head)
|
| 149 |
+
print("-" * len(head))
|
| 150 |
+
ordered = [g for g, _ in GROUP_RULES] + [GROUP_OTHER]
|
| 151 |
+
for g in ordered:
|
| 152 |
+
p = r["group_params"].get(g, 0)
|
| 153 |
+
if p == 0:
|
| 154 |
+
continue
|
| 155 |
+
n = r["group_tensor_count"].get(g, 0)
|
| 156 |
+
pct = 100 * p / r["total_elems"] if r["total_elems"] else 0
|
| 157 |
+
print(f"{g:46s}{n:9d}{p:14,d}{pct:6.1f}%")
|
| 158 |
+
|
| 159 |
+
if r["film_params"]:
|
| 160 |
+
pct = 100 * r["film_params"] / r["total_elems"]
|
| 161 |
+
print("-" * len(head))
|
| 162 |
+
print(f"{'(cắt ngang) FiLM cond_encoder':46s}"
|
| 163 |
+
f"{'':>9s}{r['film_params']:14,d}{pct:6.1f}%")
|
| 164 |
+
print(" ^ nằm rải trong down/mid/up ở trên — KHÔNG cộng thêm vào tổng")
|
| 165 |
+
print()
|
| 166 |
+
|
| 167 |
+
# Ước tính VRAM lúc train: trọng số + gradient + 2 hệ số Adam (m, v) = 4x.
|
| 168 |
+
# Đây chỉ là phần do THAM SỐ đóng góp — activations (phụ thuộc batch_size,
|
| 169 |
+
# số camera, độ phân giải ảnh) thường lớn hơn nhiều và KHÔNG được tính ở đây.
|
| 170 |
+
b = r["bytes_per_elem"]
|
| 171 |
+
w_mib = r["total_elems"] * b / 1024**2
|
| 172 |
+
print(f"--- Ước tính VRAM do THAM SỐ đóng góp ({r['dominant_dtype']}, chưa tính activations) ---")
|
| 173 |
+
print(f"Trọng số: {w_mib:8.1f} MiB (~{w_mib/1024:.2f} GiB)")
|
| 174 |
+
print(f"Inference (chỉ trọng số): {w_mib:8.1f} MiB (~{w_mib/1024:.2f} GiB)")
|
| 175 |
+
print(f"Training (+grad +2x Adam): {4*w_mib:8.1f} MiB (~{4*w_mib/1024:.2f} GiB)")
|
| 176 |
+
print()
|
| 177 |
+
print("Lưu ý khi đọc con số trên:")
|
| 178 |
+
print(" - Activations (ảnh camera qua ResNet-18) thường lớn hơn khoản này")
|
| 179 |
+
print(" nhiều lần khi train; đây là sàn dưới, KHÔNG phải tổng VRAM.")
|
| 180 |
+
print(" - Isaac Sim chạy song song cũng chiếm VRAM đáng kể (render 4 camera).")
|
| 181 |
+
print(" - Diffusion Policy lặp U-Net num_inference_steps lần mỗi bước inference:")
|
| 182 |
+
print(" không tăng đỉnh VRAM nhưng gây phân mảnh bộ nhớ -> OOM sớm hơn lý thuyết.")
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def print_comparison(reports: list) -> None:
|
| 186 |
+
print("=== So sánh nhiều file ===")
|
| 187 |
+
head = f"{'File':30s}{'Tổng phần tử':>16s}{'MiB':>10s}"
|
| 188 |
+
print(head)
|
| 189 |
+
print("-" * len(head))
|
| 190 |
+
for r in reports:
|
| 191 |
+
name = r["path"].split("/")[-1]
|
| 192 |
+
print(f"{name:30s}{r['total_elems']:16,d}{r['total_bytes']/1024**2:10.1f}")
|
| 193 |
+
if len(reports) == 2:
|
| 194 |
+
a, b = reports
|
| 195 |
+
if b["total_elems"]:
|
| 196 |
+
print(f"\nTỉ lệ {a['path'].split('/')[-1]} / {b['path'].split('/')[-1]}: "
|
| 197 |
+
f"{a['total_elems']/b['total_elems']:.3f}x")
|
| 198 |
+
print()
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
if __name__ == "__main__":
|
| 202 |
+
if len(sys.argv) < 2:
|
| 203 |
+
print("Dùng: python3 count_params_from_file.py <model1.safetensors> [model2.safetensors ...]")
|
| 204 |
+
sys.exit(1)
|
| 205 |
+
|
| 206 |
+
all_reports = []
|
| 207 |
+
for p in sys.argv[1:]:
|
| 208 |
+
rpt = count_params(p)
|
| 209 |
+
all_reports.append(rpt)
|
| 210 |
+
print_report(rpt)
|
| 211 |
+
print("=" * 70)
|
| 212 |
+
print()
|
| 213 |
+
|
| 214 |
+
if len(all_reports) > 1:
|
| 215 |
+
print_comparison(all_reports)
|