MinkowskiEngine demo ModelNet40分类

MinkowskiEngine demo ModelNet40分类

本文将看一个简单的演示示例,该示例训练用于分类的3D卷积神经网络。输入是稀疏张量,卷积也定义在稀疏张量上。该网络是以下体系结构的扩展,但具有剩余的块和更多的层。

 

 

 创建ModelNet40数据加载器

首先,需要创建一个数据加载器,以返回网格的稀疏张量表示。如果仅使用顶点,则3D模型的网格表示可能会稀疏。

首先以相同的密度采样点。

def resample_mesh(mesh_cad, density=1):

    '''

    https://chrischoy.github.io/research/barycentric-coordinate-for-mesh-sampling/

    Samples point cloud on the surface of the model defined as vectices and

    faces. This function uses vectorized operations so fast at the cost of some

    memory.

 

    param mesh_cad: low-polygon triangle mesh in o3d.geometry.TriangleMesh

    param density: density of the point cloud per unit area

    param return_numpy: return numpy format or open3d pointcloud format

    return resampled point cloud

 

    Reference :

      [1] Barycentric coordinate system

      \begin{align}

        P = (1 - \sqrt{r_1})A + \sqrt{r_1} (1 - r_2) B + \sqrt{r_1} r_2 C

      \end{align}

    '''

    faces = np.array(mesh_cad.triangles).astype(int)

    vertices = np.array(mesh_cad.vertices)

 

    vec_cross = np.cross(vertices[faces[:, 0], :] - vertices[faces[:, 2], :],

                         vertices[faces[:, 1], :] - vertices[faces[:, 2], :])

    face_areas = np.sqrt(np.sum(vec_cross**2, 1))

 

    n_samples = (np.sum(face_areas) * density).astype(int)

 

    n_samples_per_face = np.ceil(density * face_areas).astype(int)

    floor_num = np.sum(n_samples_per_face) - n_samples

    if floor_num > 0:

        indices = np.where(n_samples_per_face > 0)[0]

        floor_indices = np.random.choice(indices, floor_num, replace=True)

        n_samples_per_face[floor_indices] -= 1

 

    n_samples = np.sum(n_samples_per_face)

 

    # Create a vector that contains the face indices

    sample_face_idx = np.zeros((n_samples,), dtype=int)

    acc = 0

    for face_idx, _n_sample in enumerate(n_samples_per_face):

        sample_face_idx[acc:acc + _n_sample] = face_idx

        acc += _n_sample

 

    r = np.random.rand(n_samples, 2)

    A = vertices[faces[sample_face_idx, 0], :]

    B = vertices[faces[sample_face_idx, 1], :]

    C = vertices[faces[sample_face_idx, 2], :]

 

    P = (1 - np.sqrt(r[:, 0:1])) * A + \

        np.sqrt(r[:, 0:1]) * (1 - r[:, 1:]) * B + \

        np.sqrt(r[:, 0:1]) * r[:, 1:] * C

 

    return P

上面的函数将以相同的密度对网格上的点进行采样。接下来,在量化步骤之前经历了一系列数据扩充步骤。

数据扩充

稀疏张量由两个部分组成:1)坐标和2)与这些坐标关联的特征。必须对这两个组件都应用数据增强,以最大化固定数据集的效用,并使网络对噪声具有鲁棒性。

这在图像数据增强中并不是什么新鲜事。对图像应用随机平移,剪切,缩放,所有这些都是坐标数据扩充。颜色失真(例如色平移,颜色通道上的高斯噪声,色相饱和度增强)都具有数据增强功能。

由于在ModelNet40数据集中只有坐标作为数据,将仅应用坐标数据增强。

class RandomRotation:

 

    def _M(self, axis, theta):

        return expm(np.cross(np.eye(3), axis / norm(axis) * theta))

 

    def __call__(self, coords, feats):

        R = self._M(

            np.random.rand(3) - 0.5, 2 * np.pi * (np.random.rand(1) - 0.5))

        return coords @ R, feats

 

 

class RandomScale:

 

    def __init__(self, min, max):

        self.scale = max - min

        self.bias = min

 

    def __call__(self, coords, feats):

        s = self.scale * np.random.rand(1) + self.bias

        return coords * s, feats

 

 

class RandomShear:

 

    def __call__(self, coords, feats):

        T = np.eye(3) + np.random.randn(3, 3)

        return coords @ T, feats

 

 

class RandomTranslation:

 

    def __call__(self, coords, feats):

        trans = 0.05 * np.random.randn(1, 3)

        return coords + trans, feats

训练ResNet进行ModelNet40分类

主要训练功能很简单。没有使用基于时间的训练,而是使用了基于迭代的训练。与基于时间的训练相比,基于迭代的训练的一个优势在于,训练逻辑独立于批处理大小。

def train(net, device, config):

    optimizer = optim.SGD(

        net.parameters(),

        lr=config.lr,

        momentum=config.momentum,

        weight_decay=config.weight_decay)

    scheduler = optim.lr_scheduler.ExponentialLR(optimizer, 0.95)

 

    crit = torch.nn.CrossEntropyLoss()

 

   ...

 

    net.train()

    train_iter = iter(train_dataloader)

    val_iter = iter(val_dataloader)

    logging.info(f'LR: {scheduler.get_lr()}')

    for i in range(curr_iter, config.max_iter):

 

        s = time()

        data_dict = train_iter.next()

        d = time() - s

 

        optimizer.zero_grad()

        sin = ME.SparseTensor(data_dict['feats'],

                              data_dict['coords'].int()).to(device)

        sout = net(sin)

        loss = crit(sout.F, data_dict['labels'].to(device))

        loss.backward()

        optimizer.step()

        t = time() - s

 

        ...

运行示例

集成所有代码块时,可以运行自主ModelNet40分类网络。

python -m examples.modelnet40 --batch_size 128 --stat_freq 100

完整的代码可以在example / modelnet40.py找到。

https://github.com/NVIDIA/MinkowskiEngine/blob/master/examples/modelnet40.py

警告

ModelNet40数据加载和体素化是训练中最耗时的部分。因此,该示例将所有ModelNet40数据缓存到内存中,这将占用大约10G的内存。

 

posted @ 2021-01-04 07:30  吴建明wujianming  阅读(299)  评论(0编辑  收藏  举报