Skip to content

Commit 54e7bee

Browse files
minsukingclaude
andcommitted
Accept torch tensors as DiNTS arch_code for weights_only=True checkpoints
TopologyConstruction.__init__ converted arch_code entries with torch.from_numpy, which raises TypeError when the architecture codes are already tensors. Checkpoints such as search_code_18590.pt from the pancreas_ct_dints_segmentation and multi_organ_segmentation bundles must be re-saved with tensors to load under torch.load(weights_only=True), so the network needs to accept both. torch.as_tensor handles numpy arrays and tensors identically, keeping the existing numpy path intact. Fixes #9025 (MONAI code side; bundle artifact re-upload tracked separately) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Minsu Kim <minsu.kim08@gmail.com>
1 parent 87060c4 commit 54e7bee

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

monai/networks/nets/dints.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,8 @@ class TopologyConstruction(nn.Module):
519519
The base class for `TopologyInstance` and `TopologySearch`.
520520
521521
Args:
522-
arch_code: `[arch_code_a, arch_code_c]`, numpy arrays. The architecture codes defining the model.
522+
arch_code: `[arch_code_a, arch_code_c]`, numpy arrays or torch tensors. The architecture codes defining
523+
the model.
523524
For example, for a ``num_depths=4, num_blocks=12`` search space:
524525
525526
- `arch_code_a` is a 12x10 (10 paths) binary matrix representing if a path is activated.
@@ -605,8 +606,8 @@ def __init__(
605606
arch_code_a = torch.ones((self.num_blocks, len(self.arch_code2out))).to(self.device)
606607
arch_code_c = torch.ones((self.num_blocks, len(self.arch_code2out), self.num_cell_ops)).to(self.device)
607608
else:
608-
arch_code_a = torch.from_numpy(arch_code[0]).to(self.device)
609-
arch_code_c = F.one_hot(torch.from_numpy(arch_code[1]).to(torch.int64), self.num_cell_ops).to(self.device)
609+
arch_code_a = torch.as_tensor(arch_code[0]).to(self.device)
610+
arch_code_c = F.one_hot(torch.as_tensor(arch_code[1]).to(torch.int64), self.num_cell_ops).to(self.device)
610611

611612
self.arch_code_a = arch_code_a
612613
self.arch_code_c = arch_code_c

tests/networks/nets/test_dints_network.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,48 @@ def test_dints_search(self, dints_grid_params, dints_params, input_shape, expect
153153
self.assertTrue(isinstance(net.weight_parameters(), list))
154154

155155

156+
class TestDintsArchCode(unittest.TestCase):
157+
"""arch_code entries loaded with `torch.load(..., weights_only=True)` are tensors, not numpy arrays."""
158+
159+
def setUp(self):
160+
self.grid_params = {
161+
"channel_mul": 0.2,
162+
"num_blocks": 6,
163+
"num_depths": 3,
164+
"device": "cpu",
165+
"use_downsample": False,
166+
"spatial_dims": 3,
167+
}
168+
_cell = Cell(1, 1, 0, spatial_dims=self.grid_params["spatial_dims"])
169+
num_paths = 3 * self.grid_params["num_depths"] - 2
170+
self.arch_code_a = np.ones((self.grid_params["num_blocks"], num_paths))
171+
self.arch_code_c = np.random.randint(len(_cell.OPS), size=(self.grid_params["num_blocks"], num_paths))
172+
173+
def test_tensor_arch_code_matches_numpy(self):
174+
grid_np = TopologyInstance(arch_code=[self.arch_code_a, self.arch_code_c], **self.grid_params)
175+
grid_pt = TopologyInstance(
176+
arch_code=[torch.as_tensor(self.arch_code_a), torch.as_tensor(self.arch_code_c)], **self.grid_params
177+
)
178+
torch.testing.assert_close(grid_pt.arch_code_a, grid_np.arch_code_a)
179+
torch.testing.assert_close(grid_pt.arch_code_c, grid_np.arch_code_c)
180+
self.assertEqual(set(grid_pt.cell_tree.keys()), set(grid_np.cell_tree.keys()))
181+
182+
def test_dints_forward_tensor_arch_code(self):
183+
grid = TopologyInstance(
184+
arch_code=[torch.as_tensor(self.arch_code_a), torch.as_tensor(self.arch_code_c)], **self.grid_params
185+
)
186+
net = DiNTS(
187+
dints_space=grid,
188+
in_channels=1,
189+
num_classes=2,
190+
spatial_dims=3,
191+
use_downsample=False,
192+
node_a=torch.ones((self.grid_params["num_blocks"] + 1, self.grid_params["num_depths"])),
193+
)
194+
result = net(torch.randn(1, 1, 16, 16, 16))
195+
self.assertEqual(result.shape, (1, 2, 16, 16, 16))
196+
197+
156198
class TestDintsTS(unittest.TestCase):
157199
@parameterized.expand(TEST_CASES_3D + TEST_CASES_2D)
158200
def test_script(self, dints_grid_params, dints_params, input_shape, _):

0 commit comments

Comments
 (0)