Hãy hình dung một công cụ 3D nhỏ trong ứng dụng web. Bạn di chuyển một điểm quanh chiếc phao và muốn biết: nó nằm trong vật chất, trong lỗ ở giữa hay ở bên ngoài? Bề mặt gần nhất cách bao xa?
Một cách biểu diễn vật thể là lưu các đỉnh và những tam giác nối chúng: đó là một lưới. Chúng ta sẽ khảo sát một biểu diễn khác. Thay vì yêu cầu mạng tạo trực tiếp các tam giác, ta dạy nó một hàm: đưa vào ba tọa độ, nó trả về một khoảng cách có dấu.
Dấu cho biết điểm nằm ở phía nào của bề mặt. Giá trị tuyệt đối cho biết khoảng cách tới bề mặt ấy. Từ hàm này, ta có thể quan sát các lát cắt của vật thể rồi trích xuất một lưới.
Câu hỏi của hướng dẫn rất cụ thể: học đúng các khoảng cách có đủ không, hay một quy tắc bổ sung về cách chúng biến thiên trong không gian sẽ cải thiện kết quả? Chúng ta sẽ so sánh hai cách tiếp cận trên một hình có hình học đã biết. Nhờ vậy, ta có thể kiểm tra các câu trả lời, chứ không chỉ nhìn xem vật thể có giống chiếc phao hay không.
Tải kho lưu trữ của hướng dẫn.
Những gì chúng ta xây dựng
Hình mục tiêu là một xuyến: hãy hình dung một ống tròn khép kín như chiếc phao. Bán kính lớn của nó là R=0,55: từ tâm vật thể đến tâm ống. Bán kính nhỏ là r=0,18: đo độ dày của ống từ tâm đến bề mặt.
Tọa độ là của thí nghiệm, không có đơn vị vật lý áp đặt. Vì thế, khoảng cách 0,01 không tự động có nghĩa là một xentimét hay một milimét.
Chúng ta huấn luyện một MLP, viết tắt của multilayer perceptron, tức mạng gồm các lớp kết nối đầy đủ. Hợp đồng của nó giống một hàm trong ứng dụng: đầu vào (x, y, z), đầu ra là số. Đầu ra vô hướng này phải âm trong vật chất, dương ở ngoài và bằng không trên bề mặt.
Hai mô hình bắt đầu từ đúng cùng một khởi tạo và thấy cùng các lô điểm. Mô hình đầu chỉ học khôi phục các khoảng cách tham chiếu. Mô hình thứ hai nhận cùng ví dụ nhưng thêm một phạt eikonal, có trọng số λ=0,1. Ta sẽ giải thích kỹ thuật ngữ này: nó không đòi hỏi hình thứ hai mà là một hành vi riêng của hàm giữa các điểm.
Cuối cùng, quy trình cung cấp hai trường đã học, các phép đo của chúng và các lưới đã trích xuất. Nó cũng kiểm tra việc huấn luyện bị ngắt có thể tiếp tục chính xác. Nó không xây giao diện web, không đọc bản quét và không tái dựng vật thể thật từ ảnh.
Tại sao huấn luyện mạng khi ta đã biết công thức của xuyến? Với vật thể này, công thức là một lời giải trực tiếp rất tốt. Ở đây, nó là tham chiếu đáng tin cậy: giúp học cơ chế nơ-ron, phát hiện lỗi và hiểu tác động của hình phạt. Hướng dẫn không nhằm chứng minh AI thay thế công thức này một cách có lợi.
Các khái niệm hữu ích
Một trường: một giá trị cho mỗi vị trí
Một trường gán một giá trị cho một vị trí. Hãy nghĩ đến hàm cho nhiệt độ ở các nơi khác nhau trong phòng. Ở đây giá trị không phải nhiệt độ mà là khoảng cách có dấu đến bề mặt xuyến.
Một SDF, viết tắt của signed distance function, dùng quy ước: −0,05 nghĩa là « trong vật chất, cách bề mặt 0,05 đơn vị »; +0,05 nghĩa là « ngoài vật chất, cùng khoảng cách »; 0 là chính bề mặt.
Hãy chú ý lỗ của chiếc phao: tâm vật thể không nhất thiết nằm trong vật chất của nó. Trong xuyến của ta, gốc tọa độ (0, 0, 0) nằm trong lỗ. Khoảng cách có dấu của nó là dương.
Dưới đây là vài giá trị tính trực tiếp bằng công thức xuyến, không phải do mạng dự đoán. Trong tọa độ trong ngoặc, dấu chấm làm dấu thập phân để tránh lẫn số với trục.
Điểm (x, y, z) |
Khoảng cách có dấu giải tích | Diễn giải |
|---|---|---|
(0.55, 0, 0) |
−0,18 |
Tâm ống: trong vật chất. |
(0.73, 0, 0) |
0 |
Bề mặt ngoài, vì 0,55 + 0,18 = 0,73. |
(0.37, 0, 0) |
0 |
Bề mặt trong, hướng về lỗ. |
(0, 0, 0) |
+0,37 |
Tâm lỗ: ngoài vật chất. |
(0.78, 0, 0) |
+0,05 |
Cách bề mặt ngoài thêm 0,05 đơn vị. |
(0.55, 0, 0.18) |
0 |
Đỉnh ống. |
Các giá trị này là tính toán hình học. Với số dấu phẩy động, một số không lý thuyết có thể hiện thành một số rất nhỏ gần không.
Một hàm đã học thay vì danh sách tam giác
Trong lớp kết nối đầy đủ, mỗi đơn vị nhận mọi giá trị từ lớp trước và kết hợp chúng bằng các hệ số điều chỉnh được. Các biến đổi phi tuyến cho phép toàn bộ mạng biểu diễn thứ khác ngoài một mặt phẳng đơn giản; mạng chính xác được cung cấp trong dự án.
Trọng số là những số điều chỉnh được của mạng. Trong huấn luyện, chúng thay đổi để đưa khoảng cách dự đoán gần khoảng cách đã biết. Sau huấn luyện, mô hình dùng trọng số này để trả lời tọa độ mới: đó là suy luận.
Ta ký hiệu hàm này là fθ(x, y, z), trong đó θ là toàn bộ tham số đã học. Nó định nghĩa một biểu diễn ngầm: bề mặt không phải danh sách lưu trực tiếp mà được xác định bởi điều kiện fθ(x, y, z) = 0.
Một isosurface tập hợp các điểm mà hàm gán cùng giá trị. Mức không là điều ta quan tâm để tìm biên vật thể. Tương tự, cố định z=0 cho phép nghiên cứu một lát cắt của trường trong một mặt phẳng.
DeepSDF trình bày nguyên lý biểu diễn liên tục bằng khoảng cách có dấu. Thí nghiệm của ta hẹp hơn nhiều: chỉ một xuyến và khoảng cách giải tích, không tái tạo toàn bộ phương pháp của bài nghiên cứu đó.
Mạng có thể được hỏi tại các tọa độ không xuất hiện trong tập huấn luyện. Điều đó không có nghĩa nó chính xác ở mọi nơi: ta phải đo lỗi, không được giả định độ tin cậy ngoài miền đã khảo sát.
Gradient không gian: giá trị thay đổi theo hướng nào?
Hãy dịch chuyển điểm một chút. Nếu khoảng cách dự đoán thay đổi nhiều, hàm có độ dốc lớn theo hướng đó. Nếu thay đổi ít, độ dốc nhỏ.
Gradient không gian, ký hiệu ∇xfθ, tập hợp ba đạo hàm theo tọa độ. Ở đây x trong ký hiệu là toàn bộ điểm, không chỉ trục đầu tiên. Ta được vectơ ba số: biến thiên theo x, y và z.
Chuẩn L2 của nó là độ dài: với gradient [a, b, c], nó bằng √(a² + b² + c²). Ví dụ [1, 0, 0] có chuẩn 1; [2, 0, 0] có chuẩn 2.
Với khoảng cách có dấu thực sự, chuẩn này bằng 1 tại nơi hàm khả vi. Trực giác là: gần bề mặt đều, đi một khoảng nhỏ theo hướng vuông góc làm khoảng cách có dấu đổi đúng bấy nhiêu. Điều này không đúng cho dịch chuyển bất kỳ: trượt dọc bề mặt không tạo cùng biến thiên.
Một số vị trí đặc biệt không có một hướng bề mặt gần nhất duy nhất. Vì vậy không nên hiểu tính chất này là yêu cầu khả vi hoàn hảo tại mọi điểm trong không gian.
Phạt eikonal: một ưu tiên, không phải bảo đảm
Hình phạt khuyến khích mạng có chuẩn gradient gần 1 tại các điểm lấy mẫu. Đây là một điều chuẩn hóa: ta không chỉ chỉ ra câu trả lời cần học mà còn một tính chất mong muốn của hàm.
Công thức của hướng dẫn là:
Mất mát tổng = MAE khoảng cách + λ × trung bình((‖∇xfθ(x)‖₂ − 1)²).
Mất mát là số mà huấn luyện cố giảm. MAE, viết tắt của mean absolute error, là trung bình sai lệch tuyệt đối so với khoảng cách tham chiếu. Phần thứ hai phạt các chuẩn quá xa 1. Với λ=0,1, giá trị thứ hai được nhân 0,1 trước khi cộng; điều đó không có nghĩa nó chiếm đúng 10 % mất mát tổng.
Tại sao giữ khoảng cách tham chiếu? Hãy lấy hai hàm đơn giản: f(x,y,z)=x và g(x,y,z)=x−0,3. Cả hai có gradient chuẩn 1, nhưng bề mặt không lần lượt ở x=0 và x=0,3. Hàm h(x,y,z)=−x giữ cùng mặt phẳng với f nhưng đảo trong và ngoài.
Vì vậy chuẩn gradient không đủ để đặt đúng bề mặt hay chọn dấu của nó. Trong thí nghiệm, khoảng cách có giám sát cung cấp thông tin này. Công trình Implicit Geometric Regularization đào sâu vai trò của ràng buộc gradient; giao thức tái tạo của nó không phải xuyến giải tích của ta.
Còn một khó khăn lập trình: để hình phạt này sửa đổi trọng số, PyTorch phải có thể lấy đạo hàm một phép tính đã chứa gradient. Đó là vai trò của create_graph=True, mà ta sẽ theo dõi trong mã.
Điều kiện tiên quyết về Docker và GPU
Bạn cần biết đọc hàm Python, thao tác bảng dữ liệu và chạy lệnh trong terminal. Các khái niệm hình học nơ-ron được giới thiệu ở đây; không cần kinh nghiệm SDF trước đó.
Quy trình dự kiến dùng Linux x86_64, Docker Compose, NVIDIA Container Toolkit, RTX 3090 24 GiB, 16 GiB RAM và 16 GiB đĩa, không tính các lớp Docker dùng chung. VRAM là bộ nhớ card đồ họa, khác RAM máy. Hãy giữ các điều kiện này để theo quy trình trình bày, thay vì hiểu giải thích là xác nhận cho cấu hình khác.
Để chọn card, hãy export GPU_DEVICE=0 hoặc UUID của nó trước chuỗi lệnh. UUID là định danh của card, cho phép chỉ định một card cụ thể. Kiểm tra môi trường khởi tạo CUDA, môi trường tính toán NVIDIA dùng ở đây, và chạy một phép tính trong container.
Nhìn thấy card từ Linux không đủ chứng minh Docker dùng được nó. Hướng dẫn chính thức NVIDIA Container Toolkit ghi lại tích hợp này với driver máy chủ.
Các tính toán GPU không dùng mạng. Container áp dụng hệ thống tệp chỉ đọc, no-new-privileges, loại bỏ Linux capabilities và giới hạn CPU, RAM, PID. Các thiết lập này giới hạn quyền và tài nguyên tiến trình; chúng không thay thế kiểm thử mô hình.
Một checkpoint là bản lưu trạng thái để tìm lại mô hình hoặc tiếp tục huấn luyện. Các checkpoint của quy trình được tạo cục bộ: đừng nạp từ nguồn tùy ý. Độ tin cậy cũng phụ thuộc nguồn gốc.
Dữ liệu: một sự thật hình học có thể kiểm tra
Ba tập điểm, ba trách nhiệm
Ở đây, một nhãn, hay label, là khoảng cách có dấu tính bằng công thức chính xác. Nó không phải loại do con người chọn cũng không phải đáp án từ mô hình khác.
Chương trình chuẩn bị ba tập với các seed riêng. Một seed khởi tạo bộ sinh giả ngẫu nhiên: cho phép lấy lại các mẫu giống nhau trong môi trường dự án.
| Tập | Số điểm | Vai trò |
|---|---|---|
Huấn luyện, hay train |
65 536 | Cung cấp ví dụ để điều chỉnh trọng số. |
| Xác thực | 16 384 | Quan sát lỗi trên tập tách biệt với học. |
| Kiểm thử | 32 768 | Đo kết quả trình bày bên dưới. |
Mỗi tập gồm một nửa điểm đều trong khối [-1,1]³, một nửa điểm dựng gần bề mặt. Nhóm đầu khảo sát thể tích; nhóm sau tập trung một phần việc học gần biên ta quan tâm.
Một lưới 64³ dành cho trích xuất. Nó biểu thị 64 × 64 × 64 = 262 144 vị trí đánh giá, không phải 262 144 ví dụ huấn luyện thêm. Nó không bao giờ được dùng để huấn luyện mạng.
Đọc hai hàm mà không nhầm lẫn
Trong đoạn trích, np là NumPy; R và r là hai bán kính định nghĩa ở trên. Dự án đầy đủ cung cấp import và hằng số.
generate_points() tạo tọa độ. Sau đó torus_sdf() tính khoảng cách tham chiếu từ tọa độ nhận được. Hàm đầu không tự trả về nhãn và cả hai không ghi tệp: việc nối chuỗi và lưu thuộc bước chuẩn bị dự án.
def torus_sdf(points):
return (
np.sqrt(
(np.sqrt(points[..., 0] ** 2 + points[..., 1] ** 2) - R) ** 2
+ points[..., 2] ** 2
)
- r
)
def generate_points(n, seed, std=0.04):
rng = np.random.default_rng(seed)
uniform = rng.uniform(-1, 1, (n // 2, 3))
u = rng.uniform(0, 2 * np.pi, n // 2)
v = rng.uniform(0, 2 * np.pi, n // 2)
surface = np.stack(
[
(R + r * np.cos(v)) * np.cos(u),
(R + r * np.cos(v)) * np.sin(u),
r * np.sin(v),
],
-1,
)
near = np.clip(surface + rng.normal(0, std, surface.shape), -1, 1)
return np.concatenate([uniform, near]).astype("float32")
Công thức tìm khoảng cách như thế nào?
Biểu thức points[..., 0] chọn tọa độ x; chỉ số 1 và 2 chọn y và z. Dấu ba chấm giữ các chiều trước đó, nên có thể tính trên một mảng điểm.
Phép tính đọc theo ba bước. √(x²+y²) trước hết cho khoảng cách tới trục đứng của xuyến. Trừ R sẽ đặt lại khoảng cách này quanh đường tròn đi qua tâm ống. Cuối cùng, √((√(x²+y²)−R)²+z²)−r đo sai khác tới bề mặt ống.
Lấy điểm (0.78, 0, 0). Khoảng cách tới trục là 0,78. Trừ 0,55 được 0,23, rồi trừ bán kính ống 0,18. Kết quả 0,05, dương. Ta đang ở ngoài.
Tại (0.55, 0, 0), phép tính cho 0−0,18=−0,18. Trong lỗ, tại gốc, nó cho 0,55−0,18=0,37. Một công thức giải thích cả ba trường hợp, không cần quy tắc riêng cho lỗ.
Tại sao thêm nhiễu vào tọa độ?
uniform chứa n // 2 điểm phân bố trong khối. Các góc u và v sau đó tạo bấy nhiêu vị trí trên bề mặt xuyến: u đi quanh chiếc phao, v đi quanh ống. np.stack(..., -1) ghép ba tọa độ trên trục cuối.
Chương trình thêm vào các vị trí này nhiễu Gauss, tức dịch chuyển ngẫu nhiên có tâm bằng không. Tham số std=0.04 là độ lệch chuẩn theo mỗi tọa độ, không phải khoảng cách tối đa cho phép tới bề mặt; tài liệu NumPy về Generator.normal nêu quy ước này. np.clip đưa tọa độ về khoảng [-1,1]. Cuối cùng hai nhóm được ghép và chuyển sang float32, số dấu phẩy động lưu trên 32 bit.
Một sắc thái hữu ích: các góc được lấy đều, nhưng điều đó không tương ứng với lấy mẫu đều theo diện tích trên toàn bề mặt xuyến. Giao thức dùng đúng bộ sinh này; ta không gán cho nó phân phối khác.
Nhiễu dịch chuyển các điểm, không dịch chuyển đáp án tham chiếu của chúng. Khi có tọa độ, khoảng cách của chúng được tính lại bằng công thức giải tích. Vì vậy ta không mô phỏng nhãn khoảng cách nhiễu như cảm biến có thể tạo ra.
Bước chuẩn bị lưu các tập và kiểm tra các điểm khác nhau giữa các partition. Tuy nhiên cả ba tập liên quan cùng một xuyến và cùng cơ chế sinh: kết quả kiểm thử tốt không đo khả năng học mọi hình 3D.
Cài đặt và kiểm tra môi trường
Giải nén kho lưu trữ và đặt terminal tại thư mục gốc. Mọi lệnh sau đều bắt đầu từ chính thư mục này. Các đoạn Python giải thích cơ chế; ZIP cung cấp dự án bao quanh chúng.
Hãy bắt đầu bằng việc chuẩn bị và kiểm tra môi trường:
sh scripts/setup.sh
docker compose config --quiet
docker compose build acquire gpu
docker compose run --rm acquire python scripts/acquire.py --config configs/minimum.yaml
docker compose run --rm gpu python scripts/check_environment.py --config configs/minimum.yaml
docker compose run --rm gpu python -m pytest -q
setup.sh chuẩn bị dự án. docker compose config --quiet kiểm tra cấu hình Compose; build xây các image mà dịch vụ acquire và gpu dùng.
Tên acquire có thể gợi các hướng dẫn tải một mô hình đã huấn luyện trước. Ở đây, nó xác thực manifest không có trọng số để tải: ta sẽ học trường từ điểm tạo cục bộ. Điều này không có nghĩa xây Docker image không bao giờ cần mạng.
Kiểm tra môi trường phải thành công trước huấn luyện. Lệnh pytest sau đó chạy các kiểm thử được cung cấp. Thành công ở bước này kiểm tra chương trình và môi trường, chưa chứng minh mạng đã học biểu diễn xuyến chính xác.
Bây giờ hãy chuẩn bị dữ liệu, xem hợp đồng mạng, rồi kiểm tra mất mát:
docker compose run --rm gpu python -m neural_sdf.cli prepare --config configs/minimum.yaml
docker compose run --rm gpu python -m neural_sdf.cli inspect-model --config configs/minimum.yaml
docker compose run --rm gpu python -m neural_sdf.cli check-loss --config configs/minimum.yaml
Ký hiệu B×3 → B nghĩa là: với một lô B điểm có ba tọa độ, thu một khoảng cách cho mỗi điểm. Ví dụ, mười điểm vào dưới dạng bảng 10×3; phải có mười khoảng cách ra.
Trong PyTorch, các bảng này là tensor: các số được tổ chức theo chiều, chương trình có thể xử lý trên GPU. Mất mát vô hướng là tensor chỉ có một giá trị.
Kiểm tra kích thước này không chỉ để trang trí. Với prediction và labels dạng B, phép trừ so mỗi dự đoán với nhãn của nó. Mảng B×1 trừ mảng B có thể tự mở rộng thành B×B: chương trình khi ấy so mọi cặp thay vì chỉ các cặp tương ứng. Đây là hiệu ứng broadcasting của PyTorch, không phải phép đo đúng cho khoảng cách của ta.
check-loss kiểm tra một hợp đồng khác: hình phạt thực sự phải có gradient hướng tới trọng số. Hãy xem cách xây kiểm tra này.
Mã trung tâm: làm cho phạt eikonal có thể huấn luyện
Hai gradient, hai câu hỏi
Trong phát triển web, cùng một dữ liệu có thể qua nhiều hàm trước khi được lưu. Ở đây ta cũng phải theo một chuỗi, nhưng là chuỗi các phép tính khả vi.
Gradient không gian trả lời « khoảng cách thành gì nếu tôi di chuyển điểm? ». Gradient của mất mát theo tham số trả lời « mất mát thay đổi thế nào nếu tôi thay trọng số? ». Optimizer dùng cái thứ hai để điều chỉnh mạng.
Hình phạt nối hai gradient: nó phụ thuộc gradient không gian, vốn lại phụ thuộc trọng số. Để học tính chất này, phải giữ sự phụ thuộc đó khi tính.
Đồ thị tính toán là dấu vết các phép toán và phụ thuộc: kết quả nào đến từ số nào, phép nhân nào hay biến đổi nào? PyTorch dùng nó để lan truyền đạo hàm. Nó không phải biểu đồ hiển thị trên màn hình.
def sdf_eikonal_loss(model, points, labels, weight=0.1, create_graph=True):
points = points.detach().requires_grad_(True)
prediction = model(points)
gradient = torch.autograd.grad(prediction.sum(), points, create_graph=create_graph)[
0
]
distance = (prediction - labels).abs().mean()
eikonal = (gradient.norm(dim=-1) - 1).square().mean()
return distance + weight * eikonal, distance, eikonal
def check_loss(config):
model = torch.nn.Linear(3, 1, bias=False).cuda()
with torch.no_grad():
model.weight.copy_(torch.tensor([[2.0, 0, 0]], device="cuda"))
x = torch.randn(10, 3, device="cuda", requires_grad=True)
gradient = torch.autograd.grad(model(x).sum(), x, create_graph=True)[0]
penalty = (gradient.norm(dim=-1) - 1).square().mean()
weight_gradient = torch.autograd.grad(penalty, model.weight)[0]
assert torch.allclose(weight_gradient, torch.tensor([[2.0, 0, 0]], device="cuda"))
return {
"affine_gradient": [2.0, 0, 0],
"penalty": float(penalty),
"weight_gradient": weight_gradient.tolist(),
}
Theo dõi sdf_eikonal_loss()
Chuẩn bị tọa độ. points.detach() cắt lịch sử tính toán có thể có; requires_grad_(True) sau đó yêu cầu theo dõi biến thiên đầu ra theo các tọa độ này. Điều đó không đóng băng trọng số mô hình và cũng không di chuyển điểm. Nó chuẩn bị đầu vào để tính đạo hàm. Tài liệu detach() nói rõ sự ngắt với đồ thị trước.
Dự đoán khoảng cách. model(points) áp dụng mạng vào tọa độ. Với hợp đồng B×3 → B, prediction và labels mang một giá trị mỗi điểm.
Tính độ dốc trong không gian. torch.autograd.grad() nhận tổng dự đoán và tọa độ. Với mạng xử lý mỗi điểm độc lập với các điểm khác trong lô, lấy đạo hàm tổng cho phép tìm gradient mỗi dự đoán theo chính điểm của nó. Kết quả có dạng B×3.
[0] ở cuối lời gọi chọn phần tử đầu của tuple PyTorch trả về, phần tương ứng với points. Nó không chỉ chọn điểm đầu tiên của lô. Tài liệu torch.autograd.grad mô tả phép tính và tham số này.
Tính hai lỗi. (prediction - labels).abs().mean() cho MAE khoảng cách. gradient.norm(dim=-1) tính một chuẩn mỗi điểm bằng cách gộp ba đạo hàm. Ta trừ 1, bình phương sai khác rồi lấy trung bình: đó là phạt eikonal.
Trả về tổng và các thành phần. Hàm trả ba số tensor: mất mát tổng, MAE và hình phạt chưa có trọng số. Giữ riêng chúng giúp thấy thành phần nào cải thiện hoặc xấu đi.
Hàm không tự cập nhật trọng số. Lời gọi sau đó loss.backward() tính gradient của chúng; optimizer mới thực hiện cập nhật. Không được nhầm tính mất mát, tính gradient của nó và áp dụng sửa đổi tham số.
Một phép tính mất mát có thể tự làm lại
Đây là ba dự đoán giả định, được chọn để giải thích công thức. Chúng không đến từ mô hình đã huấn luyện hay tệp kết quả.
| Điểm | Khoảng cách tham chiếu | Dự đoán giả định | Sai khác tuyệt đối |
|---|---|---|---|
(0.55, 0, 0) |
−0,18 |
−0,16 |
0,02 |
(0.73, 0, 0) |
0 |
0,01 |
0,01 |
(0.78, 0, 0) |
0,05 |
0,02 |
0,03 |
MAE bằng (0,02 + 0,01 + 0,03) / 3 = 0,02.
Giả sử chuẩn các gradient dự đoán tại ba điểm lần lượt là 0,8, 1,0 và 1,2. Hình phạt bằng ((0,8−1)² + (1−1)² + (1,2−1)²) / 3, xấp xỉ 0,026667.
Với λ=0,1, mất mát tổng do đó là 0,02 + 0,1 × 0,026667 ≈ 0,022667. Ta không cộng trực tiếp 0,026667: trọng số rất quan trọng.
Vì sao create_graph=True là thiết yếu ở đây
PyTorch có thể tính gradient số mà không giữ mọi phụ thuộc cần để lấy đạo hàm của nó lần nữa. Nhưng hình phạt lại phụ thuộc chính vào gradient mà sau đó ta muốn lấy đạo hàm theo trọng số.
create_graph=True xây đồ thị của phép tính đạo hàm. Vì vậy đường đi vẫn khả dụng: trọng số → khoảng cách → gradient không gian → hình phạt. Với False, ta vẫn có thể nhận một số cho hình phạt, nhưng không thể dùng đường đi ấy để huấn luyện trọng số.
Hướng dẫn chính thức Double Backward with Custom Functions đào sâu khác biệt giữa tính một đạo hàm và có thể lấy đạo hàm phép tính của nó. Phần bổ sung này giải thích cơ chế; nó không yêu cầu thay mã dự án bằng hàm autograd tùy chỉnh.
Đọc kiểm tra affine: vì sao chờ [2, 0, 0]?
check_loss() chọn một lớp tuyến tính ba đầu vào, một đầu ra và không bias. Nó đặt trọng số thành [2, 0, 0]. Hàm tính được do đó đơn giản là f(x,y,z)=2x.
Dù điểm nào, gradient không gian đều là [2, 0, 0]. Chuẩn bằng 2 và hình phạt bằng (2−1)²=1. Mười điểm ngẫu nhiên không làm kiểm tra này phức tạp: với hàm này, độ dốc là hằng.
Nhưng kiểm tra đi xa hơn: nó lấy đạo hàm hình phạt theo trọng số. Gần hệ số dương a=2, với hàm f(x,y,z)=a×x, hình phạt bằng (a−1)². Đạo hàm của nó là 2×(a−1), nên bằng 2 tại a=2. Hai thành phần còn lại bằng không. Điều đó giải thích assertion trên [[2.0, 0, 0]].
Gradient không gian và gradient hướng tới trọng số ở đây có cùng giá trị số, nhưng là hai đối tượng khác nhau. Cái đầu mô tả biến thiên trong không gian; cái sau xác nhận điều chuẩn hóa có thể tham gia học.
Kiểm tra áp dụng cho riêng hình phạt, không nhân λ. Vì vậy nó chờ 2 ở trọng số đầu, không phải 0,2. Một đầu ra mất mát có vẻ hợp lý không đủ thay assertion này.
Huấn luyện, tiếp tục, đánh giá và trích xuất
Giờ ta có thể theo chuỗi hoàn chỉnh. Lệnh đầu huấn luyện biến thể eikonal. Lệnh thứ hai huấn luyện biến thể chỉ khoảng cách, làm mốc so sánh.
Các lệnh sau ngắt huấn luyện sau bước 500, tiếp tục trạng thái đã lưu, so việc tiếp tục với quy trình tham chiếu, đánh giá hai trường, rồi trích xuất các isosurface.
docker compose run --rm gpu python -m neural_sdf.cli train --config configs/minimum.yaml
docker compose run --rm gpu python -m neural_sdf.cli train --config configs/minimum.yaml --variant distance
docker compose run --rm gpu python -m neural_sdf.cli train --config configs/minimum.yaml --stop-after 500 --run-name interrupted
docker compose run --rm gpu python -m neural_sdf.cli train --config configs/minimum.yaml --resume artifacts/checkpoints/interrupted/state.pt --run-name resumed
docker compose run --rm gpu python -m neural_sdf.cli compare-resume --config configs/minimum.yaml
docker compose run --rm gpu python -m neural_sdf.cli evaluate --config configs/minimum.yaml
docker compose run --rm gpu python -m neural_sdf.cli mesh --config configs/minimum.yaml
Một lô, hay batch, là nhóm điểm được xử lý cùng nhau. Một khởi tạo đặt trọng số trước lần cập nhật đầu. Bắt đầu cùng trọng số và đưa cùng lô cho hai biến thể giảm khác biệt nhiễu trong so sánh: ta nghiên cứu việc thêm hình phạt, không phải hai tập ví dụ khác nhau.
Tiếp tục cần nhiều hơn trọng số cuối của một lớp. Trạng thái optimizer và bộ sinh giả ngẫu nhiên là một phần quy trình được kiểm soát. Nó giống tiếp tục một xử lý ứng dụng: có kết quả gần nhất chưa đủ nếu mất trạng thái quyết định thao tác tiếp theo.
Các kết quả công bố bên dưới đến từ lần tái lập của người đọc đã xác thực chuỗi này. Hướng dẫn không đưa thời lượng huấn luyện tham chiếu hay so sánh tốc độ giữa biến thể: đừng suy ra tăng thời gian từ các điểm chất lượng đơn thuần.
Chuyển từ hàm sang tam giác thế nào?
Bước mesh đánh giá trường trên lưới độc lập 64³ rồi trích mức không. Nguyên lý marching cubes là duyệt các ô lưới và dựng tam giác từ các giá trị bao quanh mức cần tìm. Tài liệu scikit-image mô tả trích xuất này, các đỉnh và mặt mà nó trả về.
Một phép tương tự một chiều giúp hiểu nội suy. Trên đoạn dài 0,1, giả sử giá trị hai đầu là −0,02 và +0,03. Nội suy tuyến tính đặt số không tại 0,02 / (0,02 + 0,03) = 0,4 của đoạn, tức 0,04 từ đầu thứ nhất. Ví dụ này giải thích nguyên lý; không phải phép đo lưới của hướng dẫn.
Lưới là xấp xỉ rời rạc của trường đã học. Độ phân giải lưới ảnh hưởng điều có thể thấy hoặc bị bỏ lỡ. Tăng độ phân giải không huấn luyện lại mạng và không tự sửa khoảng cách.
Các phép đo trước kết quả
MAE toàn cục: mạng có cho đúng khoảng cách không?
MAE khoảng cách là trung bình |dự đoán − tham chiếu| trên các điểm được đánh giá. Lỗi dương và lỗi âm không triệt tiêu nhau. Trung bình càng thấp, các giá trị dự đoán càng gần khoảng cách giải tích trong quần thể xét tới.
Nó được đánh giá trên tập xác thực và kiểm thử. Nó giữ đơn vị tọa độ của thí nghiệm: không phải phần trăm hay tỷ lệ điểm phân loại đúng trong hoặc ngoài.
MAE gần bề mặt: biên hoạt động ra sao?
Phép tính chỉ giữ các điểm có khoảng cách giải tích thỏa ngưỡng gần được khai báo trong dự án. Sau đó lỗi được lấy trung bình trên quần thể con này.
Ngưỡng số không được lặp lại trong bài: hãy xem ngưỡng của quy trình cung cấp thay vì tự chọn sau khi đọc kết quả. Quan trọng nhất, chọn lọc dựa trên khoảng cách tham chiếu, không phải khoảng cách mô hình tự ước lượng. Một dự đoán sai không được tự loại khỏi đánh giá của chính nó.
Đừng nhầm « một nửa điểm được tạo gần bề mặt » với « số điểm được ngưỡng này giữ lại ». Dịch chuyển Gauss không tạo một dải nghiêm ngặt; vài điểm đều cũng có thể gần bề mặt. Con số công bố 13 832 điểm đến từ chọn lọc đánh giá, không phải chỉ chia 32 768 cho hai.
Lỗi chuẩn gradient: độ dốc có đúng độ dài không?
Chỉ số này lấy trung bình sai khác tuyệt đối |‖∇f‖₂−1|. Nó không phải hình phạt huấn luyện, vốn lấy trung bình sai khác bình phương.
Lấy lại các chuẩn minh họa 0,8, 1,0 và 1,2. Lỗi tuyệt đối trung bình là (0,2 + 0 + 0,2) / 3 ≈ 0,133333, trong khi hình phạt bình phương xấp xỉ 0,026667. Hai số mô tả cùng ba chuẩn theo quy ước khác nhau; không thể hoán đổi.
Chỉ số này không trực tiếp kiểm tra hướng gradient hay vị trí bề mặt. Các hàm mặt phẳng trong ví dụ của ta đều có chuẩn đúng, dù bề mặt bị dịch hoặc dấu đảo ngược.
Các phép đo lưới: vật thể cuối cùng được trích xuất có giá trị thế nào?
Một đỉnh là một điểm của lưới; một mặt ở đây là tam giác nối ba đỉnh. Số lượng của chúng mô tả đầu ra trích xuất. Nhiều tam giác hơn không tự động nghĩa là chính xác hơn.
Sai khác giải tích trung bình của các đỉnh đo khoảng cách của chúng tới bề mặt thật của xuyến: với mỗi đỉnh trích xuất, tính giá trị tuyệt đối của SDF giải tích rồi lấy trung bình.
Cần xem các số này cùng MAE trường: các đỉnh lưới không phải cùng quần thể với điểm kiểm thử. Trung bình khoảng cách đỉnh tới bề mặt cũng không tự chứng nhận rằng toàn hình đã được phủ, mọi tam giác dùng được hay cấu trúc hình học đúng.
Kết quả quan sát
Các số sau là của lần tái lập r1 của hướng dẫn. Các tính toán minh họa trước đó không nằm trong các phép đo này.
So sánh hai trường trên tập kiểm thử
Tập kiểm thử chứa 32 768 điểm. Phép đo gần bề mặt áp dụng cho 13 832 điểm. Với ba chỉ số dưới đây, giá trị thấp hơn là tốt hơn.
| Phép đo trên tập kiểm thử | Chỉ khoảng cách | Khoảng cách + phạt eikonal |
|---|---|---|
| MAE khoảng cách toàn cục | 0,006337 |
0,008552 |
| MAE gần bề mặt | 0,004770 |
0,004700 |
| Lỗi tuyệt đối trung bình của chuẩn gradient | 0,024407 |
0,023419 |
Biến thể eikonal cải thiện nhẹ MAE gần bề mặt và lỗi chuẩn gradient. Tuy nhiên, MAE toàn cục của nó cao hơn. Vì vậy ràng buộc bổ sung không đồng thời cải thiện mọi chỉ số.
Có thể làm sai khác cụ thể hơn bằng phép trừ đơn giản các giá trị công bố: MAE toàn cục tăng 0,002215, MAE gần bề mặt giảm 0,000070, lỗi chuẩn giảm 0,000988. Đây là chênh lệch tính từ các số làm tròn hiển thị, không phải chiến dịch đo mới.
Bảng không cho độ phân tán qua nhiều khởi tạo độc lập. Nó cho phép mô tả sự đánh đổi quan sát được, không khẳng định sai khác nhỏ như vậy sẽ tái diễn một cách có hệ thống.
So sánh các lưới đã trích xuất
Cùng độ phân giải 64³ đã dùng để trích xuất hai lưới.
| Phép đo trích xuất | Chỉ khoảng cách | Khoảng cách + phạt eikonal |
|---|---|---|
| Đỉnh | 5 448 | 5 864 |
| Mặt | 10 896 | 11 728 |
| Sai khác giải tích trung bình của đỉnh | 0,005149 |
0,005189 |
Biến thể eikonal tạo nhiều đỉnh và mặt hơn, nhưng sai khác giải tích trung bình của đỉnh lại cao hơn đôi chút. Do đó, trong phép đo này, lưới dày hơn không phải lưới gần bề mặt hơn.
Mức cải thiện nhẹ MAE gần bề mặt không được trình bày như lợi ích đã chứng minh trên lưới. Điểm đầu áp dụng cho các điểm kiểm thử được chọn; điểm thứ hai áp dụng cho đỉnh từ trích xuất. Chúng không cùng quần thể cũng không cùng phép tính.
Việc tiếp tục sau ngắt đã tìm lại các tham số với lỗi tối đa bằng không, cùng optimizer, bộ sinh và khởi tạo chung. Kết quả này xác thực việc tiếp tục trong quy trình đã thử; nó không là phép đo bổ sung về chất lượng hình học.
Những gì chúng ta đã học
Ta đã học biểu diễn một hình bằng hàm tọa độ, rồi trở về tam giác. Mạng không nhận một lệnh mơ hồ như « vẽ một xuyến »: nó học các cặp điểm–khoảng cách mà đáp án có thể kiểm soát.
Phạt eikonal thêm một ưu tiên về biến thiên không gian của hàm này. Để nó ảnh hưởng trọng số, phép tính phải giữ khả năng lấy đạo hàm gradient không gian. Đây chính là điều kiểm tra affine làm rõ.
Một ràng buộc hữu ích không bảo đảm mọi điểm số đều tiến bộ. Trong thí nghiệm này, biến thể được điều chuẩn cải thiện nhẹ hai chỉ báo của trường, còn chỉ khoảng cách giữ MAE toàn cục tốt nhất và sai khác trung bình thấp nhất của các đỉnh trích xuất.
Cuối cùng, ba đối tượng phải tách biệt: hình giải tích làm tham chiếu, trường nơ-ron xấp xỉ nó và lưới lấy mẫu từ trường ấy. Lưới có thể che giấu hoặc tạo artefact mà trọng số không đổi. Vì vậy kết quả trên xuyến này không xác thực tái dựng bản quét thật.
Bài tập: giả thuyết trước lệnh
Trước khi chạy lệnh, hãy quay lại hàm của kiểm tra: f(x,y,z)=2x. Dự đoán điều xảy ra khi create_graph=True thành False.
Hãy ghi riêng câu trả lời: khoảng cách còn tính được không? Gradient không gian còn có thể là [2,0,0] không? Hình phạt còn có thể hiển thị 1 không? Và PyTorch còn có đường đi cần thiết để lấy đạo hàm hình phạt này theo trọng số không?
Sau đó chạy kiểm tra âm mà dự án dự kiến:
docker compose run --rm gpu python -m neural_sdf.cli exercise --config configs/exercise.yaml
Đọc trạng thái kiểm tra và đối chiếu dự đoán của bạn. Điều cần hiểu là một giá trị hình phạt vẫn có thể tính được trong khi phụ thuộc khả vi của nó vào trọng số không còn. Khi đó, lỗi dự kiến của kiểm tra là thông tin hữu ích, không phải lý do che giấu vấn đề để tiếp tục.
« Gradient không khả dụng » không có nghĩa « gradient toán học bằng không ». Trong trường hợp affine, ta biết tính tay giá trị nó: nó không bằng không. Điều thiếu là đường tính toán PyTorch cần.
Hãy giữ quy trình tham chiếu. Trường hợp affine đưa ra dự đoán để kiểm tra: quan sát trạng thái thực sự do lệnh trả về, thay vì chờ một câu đầu ra riêng. Các giá trị tính tay giải thích cơ chế; chúng không thay quan sát này.
Điều chỉnh và sản xuất
Thay đổi một giả thuyết trong thí nghiệm riêng
Một phần mở rộng sư phạm là thay đổi trọng số eikonal trong bản sao dự án, đồng thời giữ dữ liệu, khởi tạo và lưới có thể so sánh. Trước khi đo, hãy ghi những chỉ số bạn nghĩ sẽ thay đổi, rồi chạy lại quy trình đã điều chỉnh.
Đề xuất này không có kết quả báo trước. Đổi λ tạo một thí nghiệm khác; điểm số của nó không được lặng lẽ thay thế điểm r1. Kho lưu trữ vẫn là tham chiếu cho tham số và lệnh của quy trình được trình bày.
Vì sao bản quét thật làm thay đổi bài toán
Ở đây, ta biết khoảng cách có dấu của bất kỳ điểm nào nhờ công thức. Tập điểm từ bản quét không tự mang cùng thông tin ấy khắp không gian. Thu nhận, đơn vị, hệ quy chiếu, nhiễu, lỗ và cách xác định trong/ngoài trở thành các chủ đề cần xử lý rõ ràng.
Trước ứng dụng thật, cũng cần xác định quyền dữ liệu, tiêu chí chấp nhận và việc dùng lưới: trực quan hóa, xử lý tiếp hay nhu cầu khác. Ngưỡng loại bỏ phải tùy vào cách dùng đó, không chỉ vào ấn tượng thị giác thuận lợi.
Vận hành còn cần quản lý truy cập artefact, sao lưu, theo dõi lỗi và drift, cùng đo tải. Mã được cung cấp không có pipeline quét, xác thực lâm sàng hoặc công nghiệp, cũng không có dịch vụ phơi ra.
Khắc phục sự cố và dọn dẹp
Nếu CUDA lỗi, kiểm tra driver, card được chọn và tích hợp Docker theo hướng dẫn NVIDIA Container Toolkit. Sau đó chạy lại kiểm tra môi trường trước khi sửa mạng.
Nếu lỗi gradient xuất hiện, hãy phân biệt giá trị số sai với đường lấy đạo hàm thiếu. Kiểm tra requires_grad, create_graph và việc không vô hiệu hóa gradient quanh phép tính liên quan. Dự đoán khoảng cách không tính đạo hàm và đánh giá phạt gradient không đòi hỏi cùng điều kiện chạy. Đừng bỏ assertion của kiểm tra affine để có thành công bề ngoài.
Nếu không có mức không trong lưới, hãy xem giá trị trường, miền được hỏi và mô hình đã nạp trước khi kết luận học thất bại. Trích xuất không thể cho thấy lần đi qua không mà các giá trị lấy mẫu không cho phép tìm lại. Đừng tự động thay mức không bằng ngưỡng khác: bạn sẽ đổi bề mặt cần tìm.
Sau bài tập, tạo các hình, kiểm tra checkpoint, xem trước dọn dẹp, rồi dừng dịch vụ:
docker compose run --rm gpu python lesson/figures/plot.py
docker compose run --rm gpu python scripts/check_checkpoints.py
docker compose run --rm gpu python scripts/clean.py --scope generated --dry-run
docker compose down --remove-orphans
--dry-run nghĩa là dọn dẹp là một bản xem trước: nó giữ các tệp cục bộ. Hãy xem những gì được thông báo trước bất kỳ xóa nào sau đó. Lệnh cuối dừng tài nguyên Compose của dự án và các container mồ côi; đó không phải dọn sạch Docker toàn cục.
Giữ các đầu ra cần cho so sánh, đặc biệt trước khi chạy một biến thể thí nghiệm.
Nguồn chính và tài liệu
Các tham chiếu dưới đây đào sâu khái niệm. Chúng không thay thế phiên bản và tham số trong kho lưu trữ để tái tạo kết quả r1.
Park et al. — DeepSDF trình bày biểu diễn liên tục bằng khoảng cách có dấu. Gropp et al. — Implicit Geometric Regularization nghiên cứu điều chuẩn hình học bằng một tính chất gradient. Các thí nghiệm của họ khác với xuyến giải tích trình bày ở đây.
Tài liệu PyTorch — automatic differentiation, tham chiếu torch.autograd.grad và hướng dẫn Double Backward with Custom Functions giải thích các đạo hàm và đồ thị cần cho sự lan truyền của chúng. Các trang về detach() và broadcasting bổ sung việc đọc mã.
Tham chiếu NumPy — Generator.normal mô tả phép lấy nhiễu Gauss để dịch chuyển các điểm.
Tham chiếu scikit-image — marching cubes trình bày chi tiết trích xuất isosurface từ thể tích lấy mẫu. Hướng dẫn cài đặt NVIDIA Container Toolkit liên quan truy cập GPU từ container.