Spaces:
Paused
Paused
File size: 14,895 Bytes
1cac303 b957821 1cac303 b957821 1cac303 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | # -*- coding: utf-8 -*-
# ============================================================
# patch_detected.py — แก้ repo 2DImage2BMI ให้รันได้บน Hugging Face Space
#
# ===== ต้องการอะไร =====
# repo ต้นฉบับรันบนเครื่องเราไม่ได้ ด้วย 2 เหตุผล:
# (1) import ของ 4 ชิ้นที่ build ไม่ผ่าน (SCHP/PSP/CPM/CRFRNN)
# (2) ฮาร์ดโค้ด .cuda() ไว้ -> ถ้าเครื่องไม่มี GPU จะพังทันที
# Kaggle มี T4 เลยเจอแค่ปัญหา (1) ส่วน (2) ไม่เคยโผล่
# พอย้ายมา Space ฟรีที่รันบน CPU -> เจอปัญหา (2) เต็มๆ
#
# ===== ไฟล์นี้ทำอะไร =====
# แก้ 2 ไฟล์ในrepo ให้ใช้งานได้:
# Detected.py -> ตัด import ที่ไม่มี + เปลี่ยน .cuda() เป็น auto-detect
# modeling/affine_align.py -> เปลี่ยน .cuda() เป็น auto-detect
# รันซ้ำได้ ไม่พัง (เช็ค marker ก่อนแก้ทุกครั้ง)
#
# ===== วิธีคิด =====
# *** ไม่ฮาร์ดโค้ดเป็น 'cpu' *** แต่ทำเป็น auto-detect:
# _DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
# -> มี GPU ก็ใช้ GPU / ไม่มีก็ใช้ CPU = ใช้ได้ทุก hardware ไม่ต้องแก้อีก
#
# *** ห้ามเอา SCHP/PSP/CPM/CRFRNN กลับมา แม้จะลงได้ ***
# ของพวกนั้นมีไว้ "ลบแขนออกจาก mask" ก่อนวัดสัดส่วน
# แต่โมเดล SVR ของเราเทรนบน mask ที่ "มีแขน" อยู่แล้ว
# ถ้าตอนใช้งานไปตัดแขน -> WSR/Area/H2W เปลี่ยนหมด -> SVR เจอเลขที่ไม่เคยเห็น -> BMI มั่ว
# กฎเหล็ก: ตอนใช้ต้องเหมือนตอนเทรนเป๊ะ รวมถึงข้อบกพร่องด้วย
# ============================================================
import sys
import os
MARK = '# [cut] ' # เครื่องหมายของบรรทัดที่ถูกคอมเมนต์ทิ้ง
TAG = '# [patched]' # เครื่องหมายของบรรทัดที่ถูกแก้ (ใช้เช็คว่า patch ไปแล้วหรือยัง)
# ---------- โค้ดตรวจหา device ที่จะแทรกเข้าไป ----------
DEVICE_SNIPPET = (
f"import torch as _torch {TAG}\n"
f"_DEVICE = 'cuda' if _torch.cuda.is_available() else 'cpu' {TAG} auto-detect: มี GPU ใช้ GPU / ไม่มีใช้ CPU"
)
# ============================================================
# รายการแก้ของแต่ละไฟล์
# ============================================================
PATCHES = {
# ---------------- ไฟล์หลัก ----------------
'Detected.py': {
# (1) คอมเมนต์ทิ้ง: import ของที่เราไม่มี + โค้ดที่เรียกใช้มัน
'cut': [
'from Human_Parse import HumanParser',
'from PSP import HumanParser_PSP',
'from CPM import CPM_Keypoint',
'from CRFRNN import CRFRNN_Contour',
'self._HumanParser = HumanParser()',
'Arms_mask = self._HumanParser.Arms_detect(img)',
'ContourOutput = ContourOutput ^ Arms_mask',
],
# (2) แทรกโค้ดตรวจ device ต่อท้าย import สุดท้าย
'insert_after': 'import time',
# (3) เปลี่ยน .cuda() เป็น .to(_DEVICE)
# ⚠️ ห้ามเติมคอมเมนต์ต่อท้ายข้อความที่แทน ถ้าจุดนั้นอยู่ "กลางนิพจน์"
# เพราะคอมเมนต์จะกลืนส่วนที่เหลือของบรรทัด -> วงเล็บไม่ปิด -> SyntaxError
# (เคยพลาดมาแล้วตอนเทส) ใส่ได้เฉพาะบรรทัดที่จบในตัวเอง
'replace': [
('Model = Pose2Seg().cuda()',
'Model = Pose2Seg().to(_DEVICE)'),
# detectron2 default เป็น cuda -> ต้องสั่งให้ตรง device ด้วย
# (บรรทัดนี้เติม tag ได้ เพราะเป็นบรรทัดเต็มที่จบในตัวเอง)
('cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url(key_file)',
'cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url(key_file)\n'
f' cfg.MODEL.DEVICE = _DEVICE {TAG}'),
],
},
# ---------------- ไส้ของ Pose2Seg: ตัวโมเดลหลัก ----------------
# เจอทีหลัง (7 ส.ค. รอบ 2) — ตอนแรก patch แค่ 2 ไฟล์ แล้วยังพัง
# "Found no NVIDIA driver" เพราะไฟล์นี้ยังเรียก .cuda(0) และ torch.load แบบไม่บอก device
'modeling/build_model.py': {
'cut': [],
'insert_after': 'import torch.nn.functional as F',
'replace': [
# .cuda(0) มี 5 จุด (บรรทัด 47, 50, 178, 191, 218) — replace ทีเดียวได้หมด
('.cuda(0)', '.to(_DEVICE)'),
# ===== หัวใจ: ตัวที่ทำให้ error จริง =====
# pose2seg_release.pkl ถูกเซฟจากเครื่องที่มี GPU
# torch.load แบบไม่ใส่ map_location จะพยายามคืนค่าลง GPU เดิม
# -> เครื่องไม่มี GPU = "Found no NVIDIA driver"
('pretrained_dict = torch.load(path)',
'pretrained_dict = torch.load(path, map_location=_DEVICE)'),
],
},
# ---------------- ไส้ของ Pose2Seg: การจัดตำแหน่งภาพ ----------------
'modeling/affine_align.py': {
'cut': [],
'insert_after': 'import torch.nn.functional as F',
# ทั้งสองจุดอยู่กลางนิพจน์ -> ห้ามเติมคอมเมนต์ต่อท้าย
'replace': [
('torch.from_numpy(Hs_new).cuda()',
'torch.from_numpy(Hs_new).to(_DEVICE)'),
('align_corners=True).float().cuda()',
'align_corners=True).float().to(_DEVICE)'),
],
},
}
def patch_file(path, spec):
"""
แก้ไฟล์ 1 ไฟล์ตาม spec
คืน (จำนวนที่แก้, list ของสิ่งที่ยังแก้ไม่ได้)
"""
src = open(path, encoding='utf-8').read()
n = 0
# ---------- (1) คอมเมนต์ทิ้งบรรทัดที่ไม่ต้องการ ----------
for bad in spec['cut']:
if bad in src and MARK + bad not in src: # เงื่อนไขที่ 2 = กัน patch ซ้ำ
src = src.replace(bad, MARK + bad)
n += 1
print(f' ตัด : {bad[:58]}')
# ---------- (2) แทรกโค้ดตรวจ device ----------
anchor = spec['insert_after']
if TAG not in src: # ยังไม่เคยแทรก
if anchor not in src:
return n, [f'หา anchor "{anchor}" ไม่เจอ — แทรก _DEVICE ไม่ได้']
src = src.replace(anchor, anchor + '\n' + DEVICE_SNIPPET, 1) # แทรกครั้งเดียวพอ
n += 1
print(f' แทรก : _DEVICE (auto-detect) ต่อจาก "{anchor}"')
# ---------- (3) เปลี่ยน .cuda() -> .to(_DEVICE) ----------
for old, new in spec['replace']:
if old in src and new not in src:
src = src.replace(old, new)
n += 1
print(f' เปลี่ยน: {old[:50]}')
open(path, 'w', encoding='utf-8').write(src)
# ---------- ตรวจซ้ำว่าไม่เหลืออะไรที่จะทำให้พัง ----------
left = []
for bad in spec['cut']:
if bad in src and MARK + bad not in src:
left.append(f'ยังไม่ถูกตัด: {bad}')
# ตรวจจาก "ข้อความใหม่ต้องมีอยู่จริง" ไม่ใช่ "ข้อความเก่าต้องหายไป"
# เพราะบางเคสข้อความเก่าเป็นส่วนหนึ่งของข้อความใหม่ (เช่น cfg.MODEL.WEIGHTS)
# ถ้าเช็คว่าเก่าหายไป จะฟ้องผิดทั้งที่ patch สำเร็จแล้ว
for _, new in spec['replace']:
if new not in src:
left.append(f'ยังไม่ถูกเปลี่ยนเป็น: {new.splitlines()[0][:50]}')
# ตรวจขั้นสุดท้าย: ต้องไม่เหลือ .cuda() ในไฟล์ที่อยู่ในเส้นทางรันจริง
for i, line in enumerate(src.splitlines(), 1):
if '.cuda()' in line and not line.strip().startswith('#'):
left.append(f'ยังเหลือ .cuda() บรรทัด {i}: {line.strip()[:50]}')
# ตรวจว่าไฟล์ยัง compile ผ่าน (กันพลาดแบบเติมคอมเมนต์กลางนิพจน์)
try:
compile(src, path, 'exec')
except SyntaxError as e:
left.append(f'SyntaxError บรรทัด {e.lineno}: {e.msg}')
return n, left
def main(repo):
# รับได้ทั้ง path ของ repo และ path ของ Detected.py (เผื่อเรียกแบบเดิม)
if repo.endswith('.py'):
repo = os.path.dirname(repo)
if not os.path.isdir(repo):
print(f'❌ ไม่เจอโฟลเดอร์ repo: {repo}')
return 1
print(f'patch repo: {repo}\n')
total, problems = 0, []
for rel, spec in PATCHES.items():
p = os.path.join(repo, rel)
print(f' [{rel}]')
if not os.path.exists(p):
print(f' ❌ ไม่เจอไฟล์')
problems.append(f'ไม่เจอ {rel}')
continue
n, left = patch_file(p, spec)
total += n
problems += [f'{rel}: {x}' for x in left]
print(f' -> แก้ไป {n} จุด' if n else ' -> ไม่มีอะไรต้องแก้ (patch ไปแล้ว)')
print()
if problems:
print('❌ มีปัญหา:')
for x in problems:
print(f' - {x}')
return 1
print(f'✅ patch สำเร็จ รวม {total} จุด')
# ============================================================
# ตรวจรอบสุดท้าย: สแกน "ทั้ง repo" หา .cuda ที่ยังเหลือ
#
# ทำไมต้องมี: ตอนแรกผม patch แค่ Detected.py + affine_align.py
# แล้วมั่นใจว่าครบ -> ที่จริง modeling/build_model.py ยังมี .cuda(0) อีก 5 จุด
# -> deploy ไปแล้วเจอ "Found no NVIDIA driver" เสียเวลาอีกรอบ
# การสแกนทั้ง repo ทำให้เห็นทุกจุดตั้งแต่ตอน patch ไม่ต้องรอไปพังบนเซิร์ฟเวอร์
#
# ข้ามโค้ดใต้ if __name__ == '__main__' เพราะไม่ถูกรันตอน import
# ============================================================
print('\n --- สแกนทั้ง repo หา .cuda ที่ยังเหลือ ---')
leftover = []
for dirpath, dirnames, files in os.walk(repo):
dirnames[:] = [d for d in dirnames if d not in ('__pycache__', '.git')]
for fn in files:
if not fn.endswith('.py'):
continue
p = os.path.join(dirpath, fn)
rel = os.path.relpath(p, repo).replace('\\', '/')
try:
lines = open(p, encoding='utf-8').read().splitlines()
except Exception:
continue
in_main = False
for i, line in enumerate(lines, 1):
if line.startswith("if __name__"):
in_main = True # ตั้งแต่บรรทัดนี้ลงไปไม่ถูกรันตอน import
# ข้าม: โค้ดใน __main__ / คอมเมนต์ / บรรทัดที่เรา patch เข้าไปเอง
if in_main or line.strip().startswith('#') or TAG in line:
continue
if '.cuda' in line:
leftover.append(f'{rel}:{i} {line.strip()[:60]}')
if leftover:
print(' ⚠️ ยังเหลือ .cuda (เช็คว่าไฟล์นี้ถูก import ตอนรันจริงไหม):')
for x in leftover:
print(f' {x}')
print(' หมายเหตุ: ถ้าไฟล์นั้นไม่มีใคร import ก็ปล่อยได้ ไม่ต้องแก้')
else:
print(' ✅ ไม่เหลือ .cuda ที่ไหนเลย')
print('\n✅ ตรวจแล้ว: ไม่เหลือ import ที่จะพัง และ .cuda ในไฟล์ที่ patch ถูกแก้ครบ')
return 0
if __name__ == '__main__':
target = sys.argv[1] if len(sys.argv) > 1 else '/home/user/app/2DImage2BMI-main'
sys.exit(main(target))
|