目录
GitHub代码链接:github/pointnet.pytorch
pointnet论文链接:https://arxiv.org/abs/1612.00593
一、安装CPU版PyTorch
首先安装Pytorch,注意要是CPU版本
1、在anaconda中创建环境
先进入Anaconda Prompt,然后输入命令行
conda create -n pointnet
这里我直接使用pointnet作为环境名,没有选择python版本,有需要可以在后面添加
2、进入环境
输入命令,进入环境
conda activate pointnet
3、下载PyTorch
进Pytorch官网>>点击Get Started>>选择电脑的各项
把下面的【运行以下命令】复制到anaconda中,程序自动开始下载PyTorch。
conda install pytorch torchvision torchaudio cpuonly -c pytorch
4、验证安装
输入以下命令
import torch
import torchvision
print(torch.__version__)
print(torch.cuda.is_available())
运行结果如下,False表示无GPU
至此,PyTorch安装成功。
二、配置PointNet环境
1、准备数据集
github上使用以下代码下载数据集:
cd scripts
bash downloads.sh
但因为我是Windows系统,所以无法使用bash命令。所以需要手动下载shapenet数据集:下载链接
(下载完成后记得把数据集放入point.pytorch文件夹中)
2、安装PointNet
先下载好github上pointnet文件,链接:pointnet.pytorch文件
进入文件夹:
cd pointnet.pytorch
输入下面代码:
pip install -e .
三、训练PointNet
我主要使用的功能是分类功能,所以只训练了分类,如果还需要分割功能只需要将下面运行的文件由train_classification.py替换为train_segmentation.py即可,其他步骤类似。
下面开始训练分类功能:
1、修改train_classification.py文件
- 添加main函数
- 在代码开头添加下面代码,并将所有的cuda()改为to(device):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
- 将scheduler.step()和optimizer.step()调换位置
修改后的代码为:
from __future__ import print_function
import argparse
import os
import random
import torch
import torch.nn.parallel
import torch.optim as optim
import torch.utils.data
from pointnet.dataset import ShapeNetDataset, ModelNetDataset
from pointnet.model import PointNetCls, feature_transform_regularizer
import torch.nn.functional as F
from tqdm import tqdm
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
parser = argparse.ArgumentParser()
parser.add_argument(
'--batchSize', type=int, default=32, help='input batch size')
parser.add_argument(
'--num_points', type=int, default=2500, help='input batch size')
parser.add_argument(
'--workers', type=int, help='number of data loading workers', default=4)
parser.add_argument(
'--nepoch', type=int, default=250, help='number of epochs to train for')
parser.add_argument('--outf', type=str, default='cls', help='output folder')
parser.add_argument('--model', type=str, default='', help='model path')
parser.add_argument('--dataset', type=str, required=True, help="dataset path")
parser.add_argument('--dataset_type', type=str, default='shapenet', help="dataset type shapenet|modelnet40")
parser.add_argument('--feature_transform', action='store_true', help="use feature transform")
opt = parser.parse_args()
print(opt)
blue = lambda x: '\033[94m' + x + '\033[0m'
opt.manualSeed = random.randint(1, 10000) # fix seed # 生成1-10000的随机整数
print("Random Seed: ", opt.manualSeed)
random.seed(opt.manualSeed)
torch.manual_seed(opt.manualSeed)
if opt.dataset_type == 'shapenet':
dataset = ShapeNetDataset(
root=opt.dataset,
classification=True,
npoints=opt.num_points)
test_dataset = ShapeNetDataset(
root=opt.dataset,
classification=True,
split='test',
npoints=opt.num_points,
data_augmentation=False)
elif opt.dataset_type == 'modelnet40':
dataset = ModelNetDataset(
root=opt.dataset,
npoints=opt.num_points,
split='trainval')
test_dataset = ModelNetDataset(
root=opt.dataset,
split='test',
npoints=opt.num_points,
data_augmentation=False)
else:
exit('wrong dataset type')
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=opt.batchSize,
shuffle=True,
num_workers=int(opt.workers))
testdataloader = torch.utils.data.DataLoader(
test_dataset,
batch_size=opt.batchSize,
shuffle=True,
num_workers=int(opt.workers))
print(len(dataset), len(test_dataset))
num_classes = len(dataset.classes)
print('classes', num_classes)
try:
os.makedirs(opt.outf)
except OSError:
pass
classifier = PointNetCls(k=num_classes, feature_transform=opt.feature_transform)
if opt.model != '':
classifier.load_state_dict(torch.load(opt.model))
optimizer = optim.Adam(classifier.parameters(), lr=0.001, betas=(0.9, 0.999))#优化器,lr学习率
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
classifier.to(device)
num_batch = len(dataset) / opt.batchSize
for epoch in range(opt.nepoch):
optimizer.step()
for i, data in enumerate(dataloader, 0):
points, target = data
target = target[:, 0]
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
optimizer.zero_grad()
classifier = classifier.train()
pred, trans, trans_feat = classifier(points)
loss = F.nll_loss(pred, target)
if opt.feature_transform:
loss += feature_transform_regularizer(trans_feat) * 0.001
loss.backward()
scheduler.step()
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.data).cpu().sum()
print('[%d: %d/%d] train loss: %f accuracy: %f' % (
epoch, i, num_batch, loss.item(), correct.item() / float(opt.batchSize)))
if i % 10 == 0:
j, data = next(enumerate(testdataloader, 0))
points, target = data
target = target[:, 0]
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
classifier = classifier.eval()
pred, _, _ = classifier(points)
loss = F.nll_loss(pred, target)
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.data).cpu().sum()
print('[%d: %d/%d] %s loss: %f accuracy: %f' % (
epoch, i, num_batch, blue('test'), loss.item(), correct.item() / float(opt.batchSize)))
torch.save(classifier.state_dict(), '%s/cls_model_%d.pth' % (opt.outf, epoch))
total_correct = 0
total_testset = 0
for i, data in tqdm(enumerate(testdataloader, 0)):
points, target = data
target = target[:, 0]
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
classifier = classifier.eval()
pred, _, _ = classifier(points)
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.data).cpu().sum()
total_correct += correct.item()
total_testset += points.size()[0]
print("final accuracy {}".format(total_correct / float(total_testset)))
if __name__ == '__main__':
main()
2、修改train_segmentation.py文件
与修改train_classification.py文件类似,修改后的代码如下:
from __future__ import print_function
import argparse
import os
import random
import torch
import torch.nn.parallel
import torch.optim as optim
import torch.utils.data
from pointnet.dataset import ShapeNetDataset
from pointnet.model import PointNetDenseCls, feature_transform_regularizer
import torch.nn.functional as F
from tqdm import tqdm
import numpy as np
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
parser = argparse.ArgumentParser()
parser.add_argument(
'--batchSize', type=int, default=32, help='input batch size')
parser.add_argument(
'--workers', type=int, help='number of data loading workers', default=4)
parser.add_argument(
'--nepoch', type=int, default=25, help='number of epochs to train for')
parser.add_argument('--outf', type=str, default='seg', help='output folder')
parser.add_argument('--model', type=str, default='', help='model path')
parser.add_argument('--dataset', type=str, required=True, help="dataset path")
parser.add_argument('--class_choice', type=str, default='Chair', help="class_choice")
parser.add_argument('--feature_transform', action='store_true', help="use feature transform")
opt = parser.parse_args()
print(opt)
opt.manualSeed = random.randint(1, 10000) # fix seed
print("Random Seed: ", opt.manualSeed)
random.seed(opt.manualSeed)
torch.manual_seed(opt.manualSeed)
dataset = ShapeNetDataset(
root=opt.dataset,
classification=False,
class_choice=[opt.class_choice])
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=opt.batchSize,
shuffle=True,
num_workers=int(opt.workers))
test_dataset = ShapeNetDataset(
root=opt.dataset,
classification=False,
class_choice=[opt.class_choice],
split='test',
data_augmentation=False)
testdataloader = torch.utils.data.DataLoader(
test_dataset,
batch_size=opt.batchSize,
shuffle=True,
num_workers=int(opt.workers))
print(len(dataset), len(test_dataset))
num_classes = dataset.num_seg_classes
print('classes', num_classes)
try:
os.makedirs(opt.outf)
except OSError:
pass
blue = lambda x: '\033[94m' + x + '\033[0m'
classifier = PointNetDenseCls(k=num_classes, feature_transform=opt.feature_transform)
if opt.model != '':
classifier.load_state_dict(torch.load(opt.model))
optimizer = optim.Adam(classifier.parameters(), lr=0.001, betas=(0.9, 0.999))
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
classifier.to(device)
num_batch = len(dataset) / opt.batchSize
for epoch in range(opt.nepoch):
optimizer.step()
for i, data in enumerate(dataloader, 0):
points, target = data
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
optimizer.zero_grad()
classifier = classifier.train()
pred, trans, trans_feat = classifier(points)
pred = pred.view(-1, num_classes)
target = target.view(-1, 1)[:, 0] - 1
# print(pred.size(), target.size())
loss = F.nll_loss(pred, target)
if opt.feature_transform:
loss += feature_transform_regularizer(trans_feat) * 0.001
loss.backward()
scheduler.step()
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.data).cpu().sum()
print('[%d: %d/%d] train loss: %f accuracy: %f' % (
epoch, i, num_batch, loss.item(), correct.item() / float(opt.batchSize * 2500)))
if i % 10 == 0:
j, data = next(enumerate(testdataloader, 0))
points, target = data
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
classifier = classifier.eval()
pred, _, _ = classifier(points)
pred = pred.view(-1, num_classes)
target = target.view(-1, 1)[:, 0] - 1
loss = F.nll_loss(pred, target)
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.data).cpu().sum()
print('[%d: %d/%d] %s loss: %f accuracy: %f' % (
epoch, i, num_batch, blue('test'), loss.item(), correct.item() / float(opt.batchSize * 2500)))
torch.save(classifier.state_dict(), '%s/seg_model_%s_%d.pth' % (opt.outf, opt.class_choice, epoch))
## benchmark mIOU
shape_ious = []
for i, data in tqdm(enumerate(testdataloader, 0)):
points, target = data
points = points.transpose(2, 1)
points, target = points.to(device), target.to(device)
classifier = classifier.eval()
pred, _, _ = classifier(points)
pred_choice = pred.data.max(2)[1]
pred_np = pred_choice.cpu().data.numpy()
target_np = target.cpu().data.numpy() - 1
for shape_idx in range(target_np.shape[0]):
parts = range(num_classes) # np.unique(target_np[shape_idx])
part_ious = []
for part in parts:
I = np.sum(np.logical_and(pred_np[shape_idx] == part, target_np[shape_idx] == part))
U = np.sum(np.logical_or(pred_np[shape_idx] == part, target_np[shape_idx] == part))
if U == 0:
iou = 1 # If the union of groundtruth and prediction points is empty, then count part IoU as 1
else:
iou = I / float(U)
part_ious.append(iou)
shape_ious.append(np.mean(part_ious))
print("mIOU for class {}: {}".format(opt.class_choice, np.mean(shape_ious)))
if __name__ == '__main__':
main()
3、开始训练
首先先训练分类功能:
进入point.pytorch文件夹下的utils文件夹:
cd utils
输入下列代码,开始训练:
python train_classification.py --dataset=D:/downloads/pointnet.pytorch/shapenetcore_partanno_segmentation_benchmark_v0/ --nepoch=50 --dataset_type shapenet
其中dateset为数据集所在的位置,nepoch为训练轮数,dateset_type为数据集类型,根据自己的情况进行修改。
开始运行后无报错,程序开始训练。
每个epoch训练后会自动保存训练的模型,模型路径为:pointnet.pytorch\utils\cls
然后训练分割功能:
输入下列代码,开始训练:
python train_classification.py --dataset=D:/downloads/pointnet.pytorch/shapenetcore_partanno_segmentation_benchmark_v0/ --nepoch=50
需要注意,在这里不需要写数据集类型,即dataset_type不需要写。
训练结束会自动保存模型在pointnet.pytorch\utils\seg路径下。
四、测试PointNet
进入utils文件夹,输入下面代码用于测试分类功能:
python show_cls.py --model cls/cls_model_0.pth
分割功能测试:
python show_seg.py --model
其中model为你想使用的模型路径。
至此,基本完成了PointNet的环境配置和训练验证步骤。
有任何问题欢迎评论区留言告诉我,记得关注点赞收藏!!!