import time from pathlib import Path import fire import numpy as np import torch import torch.nn.functional as F import tqdm from torch import distributed, nn from torch.utils import data from torch.utils.data import DataLoader, Dataset from torchvision import datasets, transforms import horovod.torch as hvd import spconv from spconv.test_utils import generate_sparse_data class FakeSparseDataset(Dataset): def __len__(self): return 500 def __getitem__(self, idx): data_ranges = { 0: [-1, 1], 1: [0, 2], 2: [-2, 0], 3: [-2, -2], } l = np.random.randint(0, 4, size=[2]) data = generate_sparse_data([16, 64, 64], [16 * 64 * 64 // 2], 3, data_range=data_ranges[l[0]], with_dense=False) data2 = generate_sparse_data([16, 64, 64], [16 * 64 * 64 // 2], 3, data_range=data_ranges[l[1]], with_dense=False) features = np.ascontiguousarray(data["features"]).astype(np.float32) indices = np.ascontiguousarray( data["indices"][:, [3, 0, 1, 2]]).astype(np.int32) features2 = np.ascontiguousarray(data2["features"]).astype(np.float32) indices2 = np.ascontiguousarray( data2["indices"][:, [3, 0, 1, 2]]).astype(np.int32) features = np.ascontiguousarray(np.concatenate([features, features2])) indices = np.ascontiguousarray(np.concatenate([indices, indices2])) return features, indices, l class FakeClassifier(nn.Module): def __init__(self): super().__init__() self.net = spconv.SparseSequential( spconv.SubMConv3d(3, 8, 3, indice_key="subm1", padding=1, use_hash=False), nn.BatchNorm1d(8), nn.ReLU(), spconv.SparseConv3d(8, 16, 3, stride=2, padding=1, use_hash=False), nn.BatchNorm1d(16), nn.ReLU(), spconv.SubMConv3d(16, 16, 3, indice_key="subm2", padding=1, use_hash=False), nn.BatchNorm1d(16), nn.ReLU(), spconv.SparseConv3d(16, 32, 3, stride=2, padding=1, use_hash=False), nn.BatchNorm1d(32), nn.ReLU(), spconv.SubMConv3d(32, 32, 3, indice_key="subm3", padding=1, use_hash=False), nn.BatchNorm1d(32), nn.ReLU(), spconv.SparseConv3d(32, 64, 3, stride=2, padding=1, use_hash=False), nn.BatchNorm1d(64), nn.ReLU(), spconv.SubMConv3d(64, 64, 3, indice_key="subm4", padding=1, use_hash=False), nn.BatchNorm1d(64), nn.ReLU(), spconv.ToDense() # [64, 2, 8, 8] ) self.linear = nn.Linear(64 * 2 * 8 * 8, 4) def forward(self, features, indices): indices = indices.int() x = spconv.SparseConvTensor(features, indices, [16, 64, 64], 2) x = self.net(x) x = x.view(2, -1) x = self.linear(x) return x def run(): hvd.init() torch.cuda.set_device(hvd.local_rank()) np.random.seed(50051 + hvd.local_rank()) ds = FakeSparseDataset() device = torch.device('cuda') model = FakeClassifier() model.to(device) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) hvd.broadcast_parameters(model.state_dict(), root_rank=0) hvd.broadcast_optimizer_state(optimizer, root_rank=0) compression = hvd.Compression.none optimizer = hvd.DistributedOptimizer( optimizer, named_parameters=model.named_parameters(), compression=compression, op=hvd.Average) for i in tqdm.tqdm(list(range(100))): # for j in range(4): # features, indices, label = ds[(i * 4 + j) % len(ds)] features, indices, label = ds[i % len(ds)] features_t = torch.from_numpy(features) indices_t = torch.from_numpy(indices) features_t = features_t.to(device) indices_t = indices_t.to(device) target = torch.from_numpy(label).to(device) output = model(features_t, indices_t) # print(output.shape) loss = F.cross_entropy(output, target) optimizer.zero_grad() loss.backward() optimizer.step() def dev(): ds = FakeSparseDataset() for i in range(10): features, indices, label = ds[i] print(indices[:10]) features_t = torch.from_numpy(features.astype(np.float32)).cuda() indices_t = torch.from_numpy(indices.astype(np.int32)).cuda() net = FakeClassifier().cuda() net(features_t, indices_t) def main(): run() if __name__ == "__main__": fire.Fire(main)