How to delete layer in pretrained model?

Thank you ptrblck.
In this code we are replacing the last layer with fusion:

class MyEnsemble(nn.Module):
def init(self, modelA, modelB, nb_classes=4):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB
# Remove last linear layer
#The nn. Identity module will just return the input without any
#manipulation and can be used to e.g. replace other layers.
#self.modelA.classifier = nn.Conv2d(64, 128, 5)

    self.modelA.classifier = nn.Identity() #densenet201
    self.modelB.fc= nn.Identity() #resnet101
    
    # Create new classifier
    #nn. Linear(n,m) is a module that creates single layer feed forward network with n inputs and m output.

    self.classifier = nn.Linear(1920+2048, nb_classes)
    #  mat1 and mat2 shapes cannot be multiplied (16x514 and 512x4096)
    
def forward(self, x):
    x1 = self.modelA(x.clone())  # clone to make sure x is not changed by inplace methods
    x1 = x1.view(x1.size(0), -1)
    x2 = self.modelB(x)
    x2 = x2.view(x2.size(0), -1)
    x = torch.cat((x1, x2), dim=1)
    
    x = self.classifier(F.relu(x))
    return x

=================
Now in the same code, how can we change to middle layer fusion with the help of nn.Identity module?For e.g. we take two network like alexnet and googlenet, so how can we merge intermidate layer with nn.identity?

See my previous response: you can replace any layer with an nn.Identity but would have to make sure the output shape is still the expected one, so you might need to add additional processing to the activation output.

Thank you @ptrblck .
I am getting an error:
/usr/local/lib/python3.9/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or None for β€˜weights’ are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing weights=AlexNet_Weights.IMAGENET1K_V1. You can also use weights=AlexNet_Weights.DEFAULT to get the most up-to-date weights.
warnings.warn(msg)
/usr/local/lib/python3.9/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or None for β€˜weights’ are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing weights=GoogLeNet_Weights.IMAGENET1K_V1. You can also use weights=GoogLeNet_Weights.DEFAULT to get the most up-to-date weights.
warnings.warn(msg)

AttributeError Traceback (most recent call last)
in
20 β€˜β€™β€™
21
β€”> 22 num_ftrs = model_ft.classifier.in_features
23 num_ftrs1 = model_ft1.fc.in_features
24

/usr/local/lib/python3.9/dist-packages/torch/nn/modules/module.py in getattr(self, name)
1267 if name in modules:
1268 return modules[name]
β†’ 1269 raise AttributeError(β€œβ€˜{}’ object has no attribute β€˜{}’”.format(
1270 type(self).name, name))
1271

AttributeError: β€˜Sequential’ object has no attribute β€˜in_features’

My code is:

#Load a pretrained model and reset final fully connected layer.
model_ft = models.alexnet(weights=True)
model_ft1 = models.googlenet(weights=True)
β€˜β€™β€™

model_ft

ct = 0
for child in model_ft.children():
ct += 1
if ct < 4:
for param in child.parameters():
param.requires_grad = False

#frozen model_ft1
ct = 0
for child in model_ft1.children():
ct += 1
if ct < 4:
for param in child.parameters():
param.requires_grad = False
β€˜β€™β€™

num_ftrs = model_ft.classifier.in_features
num_ftrs1 = model_ft1.fc.in_features

Here the size of each output sample is set to 2.

Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).

model_ft.classifier= nn.Linear(num_ftrs, 2)
model_ft1.fc= nn.Linear(num_ftrs1, 2)

#intermediate new-----------------------
model_ft.classifier= nn.MaxPool3d(num_ftrs, 2)
model_ft1.fc= nn.MaxPool3d(num_ftrs1, 2)
#----------------------------

#model_ft = model_ft.to(device)
model = MyEnsemble(model_ft,model_ft1).to(device)

criterion = nn.CrossEntropyLoss()

β€˜β€™β€™

Freeze these models

for param in model_ft.parameters():
param.requires_grad_(False)

for param in model_ft1.parameters():
param.requires_grad_(False)
β€˜β€™β€™

Create ensemble model

model = MyEnsemble(model_ft, model_ft1)

Observe that all parameters are being optimized

optimizer_ft = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

Decay LR by a factor of 0.1 every 7 epochs

exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=20, gamma=0.1)

Thanks in advance.

mode_ft.classifier seems to be an nn.Sequential container so you would need to index it first to check the in_features of the corresponding linear layer.
PS: you can post code snippets by wrapping them into three backticks ```, which makes debugging easier.

thanks.Now, the error is:

Epoch 0/7 ----------


RuntimeError Traceback (most recent call last)

in ----> 1 model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)


6 frames

/usr/local/lib/python3.9/dist-packages/torch/nn/functional.py in _max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode, return_indices) 866 if stride is None: 867 stride = torch.jit.annotate(List[int], []) β†’ 868 return torch.max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode) 869 870

RuntimeError: non-empty 4D or 5D (batch mode) tensor expected for input.

please help.

Could you post a minimal, executable, and formatted code snippet which would reproduce the new issue, please?

here is the code:
β€˜β€™β€™
class MyEnsemble(nn.Module):
def init(self, modelA, modelB, nb_classes=4):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB
# Remove last linear layer
#The nn. Identity module will just return the input without any
#manipulation and can be used to e.g. replace other layers.
#self.modelA.classifier = nn.Conv2d(64, 128, 5)

    self.modelA.classifier = nn.Identity() #alexnet
    self.modelB.classifier= nn.Identity() #googlenet
    
    
  # Create new classifier
  #nn. Linear(n,m) is a module that creates single layer feed forward network with n inputs and m output.
  #self.classifier = nn.Linear(1920+2048, nb_classes) #Fully-connected layers, also known as linear layers.
    

    #new line added for middle layer fusion ---------------
    self.classifier = nn.MaxPool3d(4096+4096, nb_classes) # Middle layer (pooling layer)
    #self.classifier = nn.Conv3d(1920+2048, nb_classes) #First layer (a convolutional layer)
    #--------------------------

    
    #  mat1 and mat2 shapes cannot be multiplied (16x514 and 512x4096)
    
def forward(self, x):
    x1 = self.modelA(x.clone())  # clone to make sure x is not changed by inplace methods
    x1 = x1.view(x1.size(0), -1)
    x2 = self.modelB(x)
    x2 = x2.view(x2.size(0), -1)
    x = torch.cat((x1, x2), dim=1)
    
    x = self.classifier(F.relu(x))
    return x

β€˜β€™β€™
#Load a pretrained model and reset final fully connected layer.
model_ft = models.alexnet(weights=True)
model_ft1 = models.vgg11(weights=True)
β€˜β€™β€™

model_ft

ct = 0
for child in model_ft.children():
ct += 1
if ct < 4:
for param in child.parameters():
param.requires_grad = False

#frozen model_ft1
ct = 0
for child in model_ft1.children():
ct += 1
if ct < 4:
for param in child.parameters():
param.requires_grad = False
β€˜β€™β€™
#num_ftrs = list(model_ft.children())[0][:-2]

num_ftrs = model_ft.classifier[6].in_features
#num_ftrs = self.axial.model.classifier[0].in_features
#num_ftrs = model_ft.classifier.in_features
num_ftrs1 = model_ft1.classifier[6].in_features

β€˜β€™β€™

Here the size of each output sample is set to 2.

Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).

model_ft.classifier= nn.Linear(num_ftrs, 2)
model_ft1.fc= nn.Linear(num_ftrs1, 2)
β€˜β€™β€™
#intermediate new-----------------------
model_ft.classifier= nn.MaxPool3d(num_ftrs, 2)

model_ft1.classifier= nn.MaxPool3d(num_ftrs1, 2)

#----------------------------

#model_ft = model_ft.to(device)
model = MyEnsemble(model_ft,model_ft1).to(device)

criterion = nn.CrossEntropyLoss()

β€˜β€™β€™

Freeze these models

for param in model_ft.parameters():
param.requires_grad_(False)

for param in model_ft1.parameters():
param.requires_grad_(False)
β€˜β€™β€™

Create ensemble model

model = MyEnsemble(model_ft, model_ft1)

Observe that all parameters are being optimized

optimizer_ft = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

Decay LR by a factor of 0.1 every 7 epochs

exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=20, gamma=0.1)
β€˜β€™β€™
model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)
β€˜β€™β€™

The error is:

Epoch 0/7 ----------


RuntimeError Traceback (most recent call last)

in ----> 1 model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)


6 frames

/usr/local/lib/python3.9/dist-packages/torch/nn/functional.py in _max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode, return_indices) 866 if stride is None: 867 stride = torch.jit.annotate(List[int], []) β†’ 868 return torch.max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode) 869 870

RuntimeError: non-empty 4D or 5D (batch mode) tensor expected for input
β€˜β€™β€™

Please reply?

thanks

Your code is unfortunately neither executable nor properly formatted, which makes it hard to interpret which part of the code is used and which is commented out via strings.
In any case, based on the error message I would assume:

model_ft.classifier= nn.MaxPool3d(num_ftrs, 2)
model_ft1.classifier= nn.MaxPool3d(num_ftrs1, 2)

is wrong as it replaces the .classifier with a 3D module which needs (batched) 5D inputs.

sorry, here is the code:
β€˜β€™β€™
class MyEnsemble(nn.Module):
def init(self, modelA, modelB, nb_classes=4):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB

    self.modelA.classifier = nn.Identity() #alexnet
    self.modelB.classifier= nn.Identity() #vgg11
    
    
    self.classifier = nn.MaxPool3d(4096+4096, nb_classes) # Middle 
    
def forward(self, x):
    x1 = self.modelA(x.clone())          x1 = x1.view(x1.size(0), -1)
    x2 = self.modelB(x)
    x2 = x2.view(x2.size(0), -1)
    x = torch.cat((x1, x2), dim=1)
    
    x = self.classifier(F.relu(x))
    return x

β€˜β€™β€™
then
β€˜β€™β€™
model_ft = models.alexnet(weights=True)
model_ft1 = models.vgg11(weights=True)

num_ftrs = model_ft.classifier[6].in_features
num_ftrs1 = model_ft1.classifier[6].in_features

model_ft.classifier[6]= nn.MaxPool3d(num_ftrs, 2)

model_ft1.classifier[6]= nn.MaxPool3d(num_ftrs1, 2)

model = MyEnsemble(model_ft,model_ft1).to(device)

criterion = nn.CrossEntropyLoss()

model = MyEnsemble(model_ft, model_ft1)

optimizer_ft = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=20, gamma=0.1)

β€˜β€™β€™
then
β€˜β€™β€™
model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)
β€˜β€™β€™
and then error comes:

β€˜β€™β€™
Epoch 0/7 ----------


RuntimeError Traceback (most recent call last)

in ----> 1 model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)


6 frames

/usr/local/lib/python3.9/dist-packages/torch/nn/functional.py in _max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode, return_indices) 866 if stride is None: 867 stride = torch.jit.annotate(List[int], []) β†’ 868 return torch.max_pool3d(input, kernel_size, stride, padding, dilation, ceil_mode) 869 870

RuntimeError: non-empty 4D or 5D (batch mode) tensor expected for input
β€˜β€™β€™

The AlexNet and vgg11 are 2d CNNs that both finish with a classifier, i.e. one or more Linear layers. Linear layers typically take a size of (batch_size, features), whereas MaxPool3d takes (batch_size, channels, height, width, length). The error is saying you’re attempting to send features into a layer that expects 3d images.

@Ajay_krishan_gairola - What are you attempting to do with this MaxPool3d layer? Or what are you wanting it to do?

@J_Johnson Thank you for your reply. I am trying to do intermediate fusion.
I want to do fusion in between any layer. please help me with my code?

@Ajay_krishan_gairola
You could just change it to a Linear layer. Also, you can probably get rid of that relu activation and instead apply a softmax activation after the Linear layer.

@J_Johnson. Thank you for your reply. I dont want to fusion to a Linear layer. I want fusion before Linear layer.

Your forward pass is already concatenating the outputs. What do you mean by β€œfusion”?

@J_Johnson I want two CNN network connect(or add) in beetween CNN network and after this add a conv layer and Linear layer.

@J_Johnson my code is:

β€˜β€™β€™
class MyEnsemble(nn.Module):
def init(self, modelA, modelB, nb_classes=4):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB

    self.modelA.classifier[6] = nn.Identity() #alexnet
    self.modelB.classifier[6] = nn.Identity() #vgg11
    
    

    #new line added for middle layer fusion ---------------
    self.classifier = nn.MaxPool3d(4096+4096, nb_classes) # Middle layer (pooling layer)
    
    
def forward(self, x):
    x1 = self.modelA(x.clone())  # clone to make sure x is not changed by inplace methods
    x1 = x1.view(x1.size(0), -1)
    x2 = self.modelB(x)
    x2 = x2.view(x2.size(0), -1)
    x = torch.cat((x1, x2), dim=1)
    
    x = self.classifier(F.relu(x))
    return x

model_ft = models.alexnet(weights=True)
model_ft1 = models.vgg11(weights=True)

num_ftrs = model_ft.classifier[6].in_features
num_ftrs1 = model_ft1.classifier[6].in_features

model_ft.classifier[6]= nn.MaxPool3d(num_ftrs, 2)

model_ft1.classifier[6]= nn.MaxPool3d(num_ftrs1, 2)

model = MyEnsemble(model_ft,model_ft1).to(device)

criterion = nn.CrossEntropyLoss()

Create ensemble model

model = MyEnsemble(model_ft, model_ft1)

Observe that all parameters are being optimized

optimizer_ft = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

Decay LR by a factor of 0.1 every 7 epochs

exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=20, gamma=0.1)

model = train_model(model, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=8)

β€˜β€™β€™

You are running into the same issue, since x will be a 2D tensor here:

x = torch.cat((x1, x2), dim=1)

as both, x1 and x2 were flattened.
nn.MaxPool3d expects a 5D input in the shape [batch_size, channels, depth, height, width] while x has the shape [batch_size, nb_features].

This part in your code is changing the shape from whatever left modelA and modelB into (batch_size, num_features). If you want to pass that thru-data to additional Convolution layers, you will likely need to remove those lines of code and put something like:

def forward(self, x):
    x1 = self.modelA(x.clone())  # clone to make sure x is not changed by inplace methods
    x2 = self.modelB(x)
    x = torch.cat((x1, x2), dim=1)
    
    x = self.cnnC(x)
    x = x.view(x.size(0), -1)
    
    x = self.final_linear(x)
    return x

And in your init:

self.cnnC = nn.Sequentual(nn.Conv2d(in_channels, out_channels, kernel_size = (3, 3), padding = (1, 1)), nn.BatchNorm2d(out_channels), nn.ReLU())

self.final_linear=nn.LazyLinear(10)

in_channels should be the number of channels on dim = 1 of you modelA and modelB outputs combined(if you aren’t sure, use a print(x1.size()) statement). And out_channels can be set as any integer value greater than 0.

Wrote this on my phone, so could contain typos.

As per your guidance, i have made the changes below:

class MyEnsemble(nn.Module):
def init(self, modelA, modelB, nb_classes=4):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB
# Remove middle linear layer

    self.modelA.classifier[6] = nn.Identity() #alexnet
    self.modelB.classifier[6]= nn.Identity() #vgg11
    
    self.classifier = nn.MaxPool3d(4096+4096, nb_classes) # Middle 


    self.cnnC = nn.Sequentual(nn.Conv2d(in_channels, out_channels=1, kernel_size = (3, 3), padding = (1, 1)), nn.BatchNorm2d(out_channels), nn.ReLU())

    self.final_linear=nn.LazyLinear(10)

def forward(self, x):
    x1 = self.modelA(x.clone())  
            x2 = self.modelB(x)
    x = torch.cat((x1, x2), dim=1)
    
            x = self.cnnC(x)
    x = x.view(x.size(0), -1)

    x = self.final_linear(x)
    return x

=============
#Load a pretrained model and reset final fully connected layer.

model_ft = models.alexnet(weights=True)

model_ft1 = models.vgg11(weights=True)

num_ftrs = model_ft.classifier[6].in_features

num_ftrs1 = model_ft1.classifier[6].in_features

#intermediate new-----------------------

model_ft.classifier[6]= nn.MaxPool3d(num_ftrs, 2, 1, 256, 256)

model_ft1.classifier[6]= nn.MaxPool3d(num_ftrs1, 2, 1, 256, 256)

model = MyEnsemble(model_ft,model_ft1).to(device)

criterion = nn.CrossEntropyLoss()

Create ensemble model

model = MyEnsemble(model_ft, model_ft1)

Observe that all parameters are being optimized

optimizer_ft = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

Decay LR by a factor of 0.1 every 7 epochs

exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=20, gamma=0.1)

Still getting thias error:

AttributeError Traceback (most recent call last)
in
43
44 #model_ft = model_ft.to(device)
β€”> 45 model = MyEnsemble(model_ft,model_ft1).to(device)
46
47 criterion = nn.CrossEntropyLoss()

in init(self, modelA, modelB, nb_classes)
25
26
β€”> 27 self.cnnC = nn.Sequentual(nn.Conv2d(in_channels, out_channels=1, kernel_size = (3, 3), padding = (1, 1)), nn.BatchNorm2d(out_channels), nn.ReLU())
28
29 self.final_linear=nn.LazyLinear(10)

AttributeError: module β€˜torch.nn’ has no attribute β€˜Sequentual’

Also please check the code!

Thank you.