microsoft / nni

An open source AutoML toolkit for automate machine learning lifecycle, including feature engineering, neural architecture search, model compression and hyper-parameter tuning.
https://nni.readthedocs.io
MIT License
14.05k stars 1.81k forks source link

Compressed InSPyReNet model times error: AttributeError: 'Conv2d' object has no attribute 'weight' #5391

Closed 587687525 closed 1 year ago

587687525 commented 1 year ago

Describe the bug: I'm new to using nni, and I can't guarantee if there are other problems with my code, but I can't fix the current problem, so I'm here to ask. I'm using the nni AutoCompressPruner to compression of the model on https://github.com/plemeri/InSPyReNet. If it is convenient, please help me complete the compressed code of InSPyReNet model, thanks. The following error appeared :

Traceback (most recent call last):
  File "G:\Project\PC\CV\SOD\compress.py", line 233, in <module>
    main(opt, args)
  File "G:\Project\PC\CV\SOD\compress.py", line 214, in main
    pruner.compress()
  File "G:\Environment\Anaconda\lib\site-packages\nni\algorithms\compression\pytorch\pruning\auto_compress_pruner.py", line 173, in compress
    SApruner = SimulatedAnnealingPruner(
  File "G:\Environment\Anaconda\lib\site-packages\nni\algorithms\compression\pytorch\pruning\simulated_annealing_pruner.py", line 79, in __init__
    super().__init__(model, config_list)
  File "G:\Environment\Anaconda\lib\site-packages\nni\compression\pytorch\compressor.py", line 336, in __init__
    super().__init__(model, config_list, optimizer)
  File "G:\Environment\Anaconda\lib\site-packages\nni\compression\pytorch\compressor.py", line 59, in __init__
    self.reset()
  File "G:\Environment\Anaconda\lib\site-packages\nni\compression\pytorch\compressor.py", line 82, in reset
    wrapper = self._wrap_modules(layer, config)
  File "G:\Environment\Anaconda\lib\site-packages\nni\compression\pytorch\compressor.py", line 376, in _wrap_modules
    wrapper = PrunerModuleWrapper(layer.module, layer.name, layer.type, config, self)
  File "G:\Environment\Anaconda\lib\site-packages\nni\compression\pytorch\compressor.py", line 310, in __init__
    self.register_buffer("weight_mask", torch.ones(self.module.weight.shape))
  File "G:\Environment\Anaconda\lib\site-packages\torch\nn\modules\module.py", line 1269, in __getattr__
    raise AttributeError("'{}' object has no attribute '{}'".format(
AttributeError: 'Conv2d' object has no attribute 'weight'

Environment:

Reproduce the problem

import nni import numpy as np import torch from PIL import Image from nni.algorithms.compression.pytorch.pruning import AutoCompressPruner from torch.nn import ParameterList from torch.nn import functional as F from torch.optim import Adam from torch.utils.data.distributed import DistributedSampler from torchvision import transforms

from lib.InSPyReNet import RhNet_SwinB from lib.optim import PolyLr from utils.dataloader import RGB_Dataset from utils.eval import SegmentationMetric from utils.misc import load_config, to_cuda

filepath = osp.split(osp.abspath(file))[0] repopath = osp.split(filepath)[0] sys.path.append(repopath)

torch.backends.cuda.matmul.allow_tf32 = False torch.backends.cudnn.allow_tf32 = False

def trainer(model, optimizer, criterion=None): global opt, args

train_dataset = RGB_Dataset(
    root="G:/ML-Dataset/",
    sets=["DUTS-TR"],
    tfs=opt.Train.Dataset.transforms)

train_sampler = None

train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=opt.Train.Dataloader.batch_size,
                                           shuffle=True,
                                           sampler=train_sampler,
                                           num_workers=opt.Train.Dataloader.num_workers,
                                           pin_memory=opt.Train.Dataloader.pin_memory,
                                           drop_last=True)

scaler = None

scheduler = PolyLr(optimizer, gamma=0.9,
                   minimum_lr=1.0e-07,
                   max_iteration=len(train_loader) * opt.Train.Scheduler.epoch,
                   warmup_iteration=opt.Train.Scheduler.warmup_iteration)

model.train()
start = 1

for epoch in range(start, opt.Train.Scheduler.epoch + 1):
    step_iter = enumerate(train_loader, start=1)

    for i, sample in step_iter:
        optimizer.zero_grad()

        sample = to_cuda(sample)
        out = model(sample)
        out['loss'].backward()
        optimizer.step()
        scheduler.step()

        if args.local_rank <= 0 and args.verbose is True:
            step_iter.set_postfix({'loss': out['loss'].item()})

    if args.local_rank <= 0:
        makedirs(opt.Train.Checkpoint.checkpoint_dir, exist_ok=True)
        makedirs(osp.join(
            opt.Train.Checkpoint.checkpoint_dir, 'debug'), exist_ok=True)
        if epoch % opt.Train.Checkpoint.checkpoint_epoch == 0:
            model_ckpt = model.state_dict()

            state_ckpt = {'epoch': epoch + 1,
                          'optimizer': optimizer.state_dict(),
                          'scheduler': scheduler.state_dict()}

            torch.save(model_ckpt, osp.join(opt.Train.Checkpoint.checkpoint_dir, 'latest.pth'))
            torch.save(state_ckpt, osp.join(opt.Train.Checkpoint.checkpoint_dir, 'state.pth'))

if args.local_rank <= 0:
    torch.save(model.state_dict(), osp.join(opt.Train.Checkpoint.checkpoint_dir, 'latest.pth'))

def finetuner(model): backbone_params = ParameterList() decoder_params = ParameterList()

for name, param in model.named_parameters():
    if 'backbone' in name:
        backbone_params.append(param)
    else:
        decoder_params.append(param)

params_list = [{'params': backbone_params}, {
    'params': decoder_params, 'lr': opt.Train.Optimizer.lr * 10}]

optimizer = Adam(
    params_list, opt.Train.Optimizer.lr, weight_decay=opt.Train.Optimizer.weight_decay)

def evaluator(model): model.eval()

img_list = glob(f"{args.original_path}\\*")

tfs = opt.Infer.transforms
comp = []
for key, value in zip(tfs.keys(), tfs.values()):
    if value is not None:
        tf = eval(key)(**value)
    else:
        tf = eval(key)()
    comp.append(tf)
transform = transforms.Compose(comp)

time_sum = 0
metric = SegmentationMetric(2)
acc_list = []
for idx in tqdm.tqdm(range(len(img_list))):
    with open(img_list[idx], 'rb') as f:

        original = Image.open(f).convert('RGB')
        shape = original.size[::-1]
        name = osp.basename(img_list[idx])[:-4]
        label = np.array(Image.open(args.label_path + name))

        inputs = {'image': original, 'name': name, 'shape': shape, 'original': original}
        inputs = transform(inputs)
        inputs['image'] = inputs['image'].unsqueeze(0)
        if 'image_resized' in inputs.keys():
            inputs['image_resized'] = inputs['image_resized'].unsqueeze(0)

        for key in inputs.keys():
            if type(inputs[key]) == torch.Tensor:
                inputs[key] = inputs[key].cuda()

        with torch.no_grad():
            time_start = time.time()
            out = model(inputs)
            time_sum = time_sum + (time.time() - time_start)

        pred = F.interpolate(out['pred'], inputs['shape'], mode='bilinear', align_corners=True)
        pred = pred.data.cpu().numpy().squeeze()

        pred = (pred * 255).astype(np.uint8)

        metric.addBatch(pred, label)
        acc = metric.pixelAccuracy()
        acc_list.append(acc)
        metric.reset()

return np.mean(acc_list) * 100

def main(opt, args): model = InSPyReNet_SwinB(**opt.Model) model.load_state_dict(torch.load(args.weights, map_location=torch.device('cpu')), strict=True)

model = model.cuda()

backbone_params = ParameterList()
decoder_params = ParameterList()

for name, param in model.named_parameters():
    if 'backbone' in name:
        backbone_params.append(param)
    else:
        decoder_params.append(param)

params_list = [{'params': backbone_params}, {
    'params': decoder_params, 'lr': opt.Train.Optimizer.lr * 10}]

optimizer = Adam(
    params_list, opt.Train.Optimizer.lr, weight_decay=opt.Train.Optimizer.weight_decay)

config_list = [{
    'sparsity': 0.5,
}]

traced_optimizer = nni.trace(Adam)(params_list, opt.Train.Optimizer.lr,
                                   weight_decay=opt.Train.Optimizer.weight_decay)
admm_params = {
    'trainer': trainer,
    'traced_optimizer': traced_optimizer,
    'criterion': None,
    'iterations': 10,
    'training_epochs': 1
}
sa_params = {
    'evaluator': evaluator
}

pruner = AutoCompressPruner(model, config_list, 10, admm_params, sa_params)
# pruner = LevelPruner(model, config_list)

pruner.compress()
_, model, masks, _, _ = pruner.get_best_result()

torch.save(model.state_dict(), osp.join(opt.Train.Checkpoint.checkpoint_dir, 'latest.pth'))

if name == 'main': parser = argparse.ArgumentParser() parser.add_argument('--weights', type=str, default=r"./weights/InSPyReNet.pth", help="weights path") parser.add_argument('--gpu', '-g', action='store_true', default=True) parser.add_argument('--config', '-c', type=str, default='configs/RhineSOD.yaml') parser.add_argument('--imgsize', type=int, default=320, help='input image size') parser.add_argument('--thres', type=int, default=50) parser.add_argument('--original_path', type=str, default=r"G:\ML-Dataset\DUTS-TR\images", help="input image path") parser.add_argument('--label_path', type=str, default=r"G:\ML-Dataset\DUTS-TR\masks", help="input image path") parser.add_argument('--mask_path', type=str, default="./outputs/mask", help="output masked path") args = parser.parse_args()

opt = load_config(args.config)
main(opt, args)```
J-shang commented 1 year ago

hello @587687525 , is the Conv2d in your model not a pytorch built-in torch.nn.Conv2d?

587687525 commented 1 year ago

@J-shang hi,My Conv2d has been wrapped once.It looks like this:


class Conv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, groups=1, padding='same', bias=False, bn=True, relu=False):
super(Conv2d, self).__init__()
if '__iter__' not in dir(kernel_size):
kernel_size = (kernel_size, kernel_size)
if '__iter__' not in dir(stride):
stride = (stride, stride)
if '__iter__' not in dir(dilation):
dilation = (dilation, dilation)
    if padding == 'same':
        width_pad_size = kernel_size[0] + (kernel_size[0] - 1) * (dilation[0] - 1)
        height_pad_size = kernel_size[1] + (kernel_size[1] - 1) * (dilation[1] - 1)
    elif padding == 'valid':
        width_pad_size = 0
        height_pad_size = 0
    else:
        if '__iter__' in dir(padding):
            width_pad_size = padding[0] * 2
            height_pad_size = padding[1] * 2
        else:
            width_pad_size = padding * 2
            height_pad_size = padding * 2

    width_pad_size = width_pad_size // 2 + (width_pad_size % 2 - 1)
    height_pad_size = height_pad_size // 2 + (height_pad_size % 2 - 1)
    pad_size = (width_pad_size, height_pad_size)
    self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, pad_size, dilation, groups, bias=bias)
    self.reset_parameters()

    if bn is True:
        self.bn = nn.BatchNorm2d(out_channels)
    else:
        self.bn = None

    if relu is True:
        self.relu = nn.ReLU(inplace=True)
    else:
        self.relu = None

def forward(self, x):
    x = self.conv(x)
    if self.bn is not None:
        x = self.bn(x)
    if self.relu is not None:
        x = self.relu(x)
    return x

def reset_parameters(self):
    nn.init.kaiming_normal_(self.conv.weight)
Lijiaoa commented 1 year ago

@587687525 Do you still have any questions?

587687525 commented 1 year ago

I still don't know how to solve my error.

J-shang commented 1 year ago

hello @587687525 , the fastest workaround is renaming your customized Conv2d, i.e. Conv2d -> FusionConv2d

Lijiaoa commented 1 year ago

Had you tried Ning's suggestions? Expect your reply! @587687525

587687525 commented 1 year ago

您好@587687525,最快的解决方法是重命名您的自定义,即Conv2d``Conv2d -> FusionConv2d

I have solved the problem with this method, thank you very much. But my code is still giving errors elsewhere and I want to know how to fix them.#5436