How to train Inception V3 on Fastai V1?

I used to train Inception model using older fastai as follows:

from fastai.conv_learner import*
from fastai.transforms import*
from fastai.dataset import*
from fastai.plots import*
import torch
import torchvision.models as TorchModels
import numpy as np

Initial setup

PATH=’/my_path/’
bs=32
sz=299 # image size for Inception-V3

Choose torch model

inceptionModel = TorchModels.inception_v3(pretrained=True)

My dataset

np.random.seed(42)
data = ImageClassifierData.from_paths(PATH, bs=bs, trn_name =‘train’, val_name = ‘valid’, test_name = ‘test’, tfms = tfms_from_model(inceptionModel,sz))

Freeze Inception weights

for param in inceptionModel.parameters():
param.requires_grad = False

Adjust last layer to my dataset

num_ftrs = inceptionModel.AuxLogits.fc.in_features
inceptionModel.AuxLogits.fc = nn.Linear(num_ftrs, len(data.classes))

num_ftrs = inceptionModel.fc.in_features
inceptionModel.fc = nn.Linear(num_ftrs, len(data.classes))

Set model to GPU

inceptionModel = inceptionModel.cuda()

Choose criterion and metrics

loss = nn.CrossEntropy()
metrics = [accuracy]

set optimizer

opt = optim.Adam(inceptionModel.parameters(), lr = 1e-2)

Begin training

vals, ep_vals = fit(inceptionModel, data, n_epochs = 2, crit = loss, opt = opt, metrics = metrics, get_ep_vals = True)

And so on…


Now, on Fastai V1 I tried the steps:

from fastai.vision import *
from fastai.metrics import accuracy
mport torch
import torchvision.models as TorchModels
import numpy as np

Initial setup

path=’/my_path/’
bs=32
sz=299 # image size for Inception-V3

np.random.seed(42)
data = ImageDataBunch.from_folder(path, ds_transformsms(do_flip = False, flip_vert = False, max_rotate = 0.0), size = sz, bs= bs). normalize (imagenet_stats)

Choose torch model

inceptionModel = TorchModels.inception_v3(pretrained=True)

Freeze Inception weights

for param in inceptionModel.parameters():
param.requires_grad = False

Adjust last layer to my dataset

If needed uncomment the line below

inceptionModel.aux_logit = False

num_ftrs =
inceptionModel.AuxLogits.fc.in_features
inceptionModel.AuxLogits.fc = nn.Linear(num_ftrs, len(data.classes))

num_ftrs = inceptionModel.fc.in_features
inceptionModel.fc = nn.Linear(num_ftrs, len(data.classes))

Set model to GPU

inceptionModel = inceptionModel.cuda()

Show samples from dataset

data.show_batch(rows = 3, figsize = (10,7))

Classes

print(data.classes)

At this point, the errors start to pop out. Using
learn = cnn_learner(data, inceptionModel, metrics = accuracy)
In the function create_body(arch:Callable, pretrained:bool=True, cut: Optional)
When forward function from torchvision\models\inception.py produces the error:
def forward(self,x):
if self.transform_input:
—> 77 x_ch0 = torch.unsqueeze(x[:,0] *(0.229/0.5) + (0.485 - 0.5) /0.5

TypeError: ‘bool’ object is not subscriptable

When using:
learn = Learner(data, inceptionModel, metrics= accuracy)

learn.fit_one_cycle(1)

In this case, because Inception has two outputs, the model returns a tuple instead of a tensor so the the loss_bacth in fastai\basic_train.py returns:

fastai\layers.py in call(self, input, target, **kwargs)
def call(self, input:Tensor, target:Tensor, **kwargs)
—>240 input= input.transpose(self.axis,-1).contiguous()
TypeError: ‘tuple’ object has no attribute ‘transpose’

Any ideas besides disabling inceptionModel.aux_logit?

PS.: I have also tried AlexNet. It worked with Learner and fit_one_cycle, but not with interp.plot_top_losses
TypeError: ‘AlexNet’ object does not support indexing

My previous experience with older fastai and torch models says that there be ways to work around with this.

The cnn_learner function not only creates a learner, but creates a new model by ripping the head off the existing model and attaching a new one. However, it only does this for the models in fastai; thats why you are getting an error with your custom model. You should therefore create a learner using the constructor, as you do towards the end.

If no loss_func is passed to the learner constructor, it uses the function in data.loss_func, which ImageDataBunch sets as cross entropy since the label class is CategoryList. Since your model returns multiple outputs, you’ll need a custom loss function:

def my_loss(preds,target):
 a,b = preds
 return F.cross_entropy(a,target)

Then create your learner with:

 Learner(data,InceptionModel,loss_func=my_loss,metrics=accuracy)
2 Likes

Thank you!!! I could train the model!

For some reason this part (a,b = preds) returned and error (too many values to unpack).

I tried to get each value of a and b at the time with
a = preds[0]
b = preds[1]
It worked until it reach validation. I was printing a and b shapes, when I got the error:

Shape a: torch.Size([32,5])
Shape b: torch.Size([32,5])

Shape a: torch.Size([5])
Shape b: torch.Size([5])

…torch\nn\functional.py in log_softmax(…)
RuntimeError: dimension out of range (expected to be in range of [-1, 0], but got 1)

I could manage that by replacing
a,b=preds in my_loss:
By:
if isinstance(preds, tuple):
loss = sum((F.cross_entropy(o,target) for o in preds))
else:
loss = F.cross_entropy(preds,target)

return loss

which I have found here: https://discuss.pytorch.org/t/imagenet-example-with-inception-v3/1691/17

I don’t know for what exactly reason it worked, but I think the way I split preds values with a = preds[0] was not the right way.

Now, to use ClassificationInterpretation.from_learner I had to add the content from F.cross_entropy to my_loss as follows:

#Criterion For Inception-V3
loss=F.cross_entropy
def my_loss(preds,target,weight=None, size_average=None, ignore_index=-100,
reduce=None, reduction=‘mean’):
if isinstance(preds,tuple):
if size_average is not None or reduce is not None:
reduction = _Reduction.legacy_get_string(size_average, reduce)
loss1 = sum((loss(o, target, weight, None, ignore_index, None, reduction) for o in preds))
else:
if size_average is not None or reduce is not None:
reduction = _Reduction.legacy_get_string(size_average, reduce)
loss1 = loss(preds, target, weight, None, ignore_index, None, reduction)

   return loss1

Then, after training I could used te classification Interpreter:
interp = ClassificationInterpretation.from_learner(learn)

Form this point Inception-V3 and AlexNet has the same error when I try to plot top losses:

TypeError: ‘Inception3’ object does not support indexing

But it is not a problem for now, because I could train both models.

Thank you @noachr!

To get plot_top_losses to work with your model, use

interp.plot_top_losses(k=9,heatmap=False)

If heatmap is enabled, the function attempts to hook into the layers of your model by index, which fails in this case.

1 Like

Perfect! Thank you @noachr. Everything working now.

I recently got the same problem but got a different error. I followed the answers by @noachr, but got a different error:
TypeError: conv2d(): argument 'input' (position 1) must be Tensor, not bool

My code:
inceptionModel = models.inception_v3(pretrained=False) def my_loss(preds,target): a,b = press return F.cross_entropy(a,target) learn = cnn_learner(data,inceptionModel,loss_func=my_loss,metrics=accuracy)

any help would be appreciated.