Setting up Multi-label classfication using ImageDataBunch.from_csv

#1

From what I understand, ImageDataBunch.from_csv() will accept multi-label inputs by simply space-separating the classes in the labels column of the csv.

However, I have done this for my dataset so that there are multiple labels per image, and it appears that now my loss is incorrect, giving negative values.

e.g.

epoch train_loss valid_loss accuracy
1 -2.241073 -5.858467 0.473379

Is the negative loss due to the loss function itself? If so, how can I change it within fastai such that it properly handles a multi-label dataset?

In summary, I would like to know:

  1. Is fastai supposed to handle multi-label datasets “under the hood” without me explicitly changing anything? If so, why is it showing a negative loss?

  2. What loss function(s) are appropriate for multi-label datasets and how should I implement them in fastai?

0 Likes

(Anish Dalal) #2

I’m also working on a Multi-label classification problem.

Regarding your 1st Question:

  • Yes, fastai should be able to handle the dataset automatically. It actually creates a object of type ImageMultiDataset that should contain all the various labels and classes.

  • I’m not completely sure why a negative loss is showing. Are you explicitly passing in the “sep” keyword argument when calling from_csv()? Looking at the source code , I believe you need to set the sep key word argument in order for ImageMultiDataset to be used.

Regarding Your 2nd Question:

  • The ImageMultiDataset uses Pytorch’s binary_cross_entropy_with_logits for multi-label problems.

I hope this helps. :slightly_smiling_face:

1 Like

#3

Thank you, setting the ‘sep’ seemed to change my loss function to Binary Cross Entropy with Logits as you said.

However, now I am receiving a different error when trying to use learn.fit_one_cycle():
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

How did the targets change to floats? Shouldn’t they be just integers from 0 to N-1 representing N classes?

I am using fp16 training, if that makes any difference. I don’t know for sure if fp16 works with multi-label datasets because this is my first time trying.

Full traceback:

Traceback (most recent call last):-----------------------------| 0.00% [0/127 00:00<00:00]
File “hptrain-amazon-64.py”, line 91, in
HP_types, def_vals, test_vals, timestamp)
File “/home/nyc1/platform-hyperparams/scripts/hputils3.py”, line 406, in train_main
ds_size=ds_size)
File “/home/nyc1/platform-hyperparams/scripts/hputils3.py”, line 321, in train_all_models
num_eps=num_eps)
File “/home/nyc1/platform-hyperparams/scripts/hputils3.py”, line 261, in train_one_model
num_eps=num_eps)
File “/home/nyc1/platform-hyperparams/scripts/hputils3.py”, line 158, in train_pipeline
learn.fit_one_cycle(5, lr, wd=wd)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/train.py”, line 19, in fit_one_cycle
learn.fit(cyc_len, max_lr, wd=wd, callbacks=callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 161, in fit
callbacks=self.callbacks+callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 93, in fit
raise e
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 83, in fit
loss = loss_batch(model, xb, yb, loss_func, opt, cb_handler)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 22, in loss_batch
loss = loss_func(out, *yb)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/functional.py”, line 1530, in nll_loss
return torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

0 Likes

(Edwin) #4

Try changing the metric. Ran into a similar issue when also running a multi-label dataset after setting sep param in data. Think maybe accuracy and error_rate only work if the predict is a single class.

2 Likes

#5

I attempted to use Fbeta and instead got this error:

File “/home/nyc1/platform-hyperparams/scripts/hputils3.py”, line 155, in train_pipeline
learn.fit_one_cycle(5, lr, wd=wd)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/train.py”, line 19, in fit_one_cycle
learn.fit(cyc_len, max_lr, wd=wd, callbacks=callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 161, in fit
callbacks=self.callbacks+callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 93, in fit
raise e
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 88, in fit
cb_handler=cb_handler, pbar=pbar)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 54, in validate
if cb_handler and cb_handler.on_batch_end(val_losses[-1]): break
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/callback.py”, line 238, in on_batch_end
stop = np.any(self(‘batch_end’, not self.state_dict[‘train’]))
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/callback.py”, line 186, in call
if call_mets: [getattr(met, f’on_{cb_name}’)(**self.state_dict, **kwargs) for met in self.metrics]
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/callback.py”, line 186, in
if call_mets: [getattr(met, f’on_{cb_name}’)(**self.state_dict, **kwargs) for met in self.metrics]
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/callback.py”, line 270, in on_batch_end
self.val += last_target.size(0) * self.func(last_output, last_target).detach().item()
AttributeError: ‘Fbeta’ object has no attribute ‘detach’

This is on fastai v1.0.15.

0 Likes

#6

Fbeta wouldn’t go on that part of the code normally. Could you paste out your code? In particular did you instantiate the class (with parenthesis)?

0 Likes

#7

The only thing that I did was change the metrics from [accuracy] to [Fbeta]:
learn = create_cnn(*other_args, metrics=[Fbeta])

0 Likes

#8

Like I said, you didn’t instantiate the class. You should use Fbeta() if you want all the default args. Fbeta(beta=something) for another value of beta etc…

1 Like

#9

Once the class is instantiated, would I still pass it as a metric to a learner?

0 Likes

#10

Yes indeed.
Note that we just discovered a bug in Fbeta and removed it. Now you should use the function fbeta (v1.0.17).

0 Likes

#11

I modified the code to the default arguments:
create_cnn(metrics=[fbeta()])
but I am still receiving an error:
TypeError: fbeta() missing 2 required positional arguments: ‘y_pred’ and ‘y_true’

Shouldn’t the y_pred and y_true come after the learner starts training? Why would it ask for y_pred and y_true before doing anything?

This is in v1.0.18.

EDIT: I tried changing it back so it has no parenthesis:
create_cnn(metrics=[fbeta])

This runs for an epoch and then throws the following error:
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/train.py”, line 22, in fit_one_cycle
learn.fit(cyc_len, max_lr, wd=wd, callbacks=callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 162, in fit
callbacks=self.callbacks+callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 94, in fit
raise e
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 84, in fit
loss = loss_batch(model, xb, yb, loss_func, opt, cb_handler)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 22, in loss_batch
loss = loss_func(out, *yb)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/functional.py”, line 1530, in nll_loss
return torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

So the targets are still of type Float. I have confirmed that the loss function from “learn.loss_func” is actually BCE with Logits, yet the program attempts to use nll_loss. Why is this?

0 Likes

#12

Sorry I was unclear, fbeta is now a function, like accuracy, so doesn’t take parenthesis. The previous one was a class, that’s why it needed to be instantiated.

0 Likes

#13

OK. So then the following:
create_cnn(metrics=[fbeta])
is the correct usage?

What about the issue with the loss function? I printed out learn.loss_func and received:

<function binary_cross_entropy_with_logits at 0x7f7169c6eea0>

which means that it should be using BCE with logits as expected. However I still received the following error:

return torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

which suggests that it is maybe using the wrong loss function?

EDIT: I attempted manually forcing the learner to use BCE as loss function:

learn = create_cnn(data, arch, path=PATH,
metrics=[fbeta],
ps=[p1, p2],
pretrained=True,
callback_fns=[ShowGraph])
learn.loss_func = nn.BCEWithLogitsLoss

but I got this error instead:

File “hptrain-amazon-64.py”, line 84, in
learn.lr_find()
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/train.py”, line 30, in lr_find
learn.fit(a, start_lr, callbacks=[cb], **kwargs)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 162, in fit
callbacks=self.callbacks+callbacks)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 94, in fit
raise e
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 84, in fit
loss = loss_batch(model, xb, yb, loss_func, opt, cb_handler)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/fastai/basic_train.py”, line 22, in loss_batch
loss = loss_func(out, *yb)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/modules/loss.py”, line 568, in init
super(BCEWithLogitsLoss, self).init(size_average, reduce, reduction)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/modules/loss.py”, line 15, in init
self.reduction = _Reduction.legacy_get_string(size_average, reduce)
File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/functional.py”, line 48, in legacy_get_string
if size_average and reduce:
RuntimeError: bool value of Tensor with more than one value is ambiguous

0 Likes

#14

Again, nn.BCEWithLogits is a class, so you need to instantiate it to have a function :wink: .

2 Likes

#15

Yes, I had just realized that :sweat_smile:

However it still gives me the same error as not forcing it manually:

return torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

Even multilabel margin loss gives the same kind of error:

File “/home/adrian/anaconda3/envs/fastai11/lib/python3.7/site-packages/torch/nn/functional.py”, line 1863, in multilabel_margin_loss
return torch._C._nn.multilabel_margin_loss(input, target, reduction)
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 ‘target’

0 Likes

#16

What is weird is that you get to a nll_loss when we want binary crossentropy with logits (that expects target as float so this is normal). What is learn.loss_func after you have the error?

0 Likes

#17

It definitely prints out BCE loss:

<function binary_cross_entropy_with_logits at 0x7f7169c6eea0>

0 Likes

#18

OK I think I have found the problem. I used this code to try and debug:

try:
learn.fit_one_cycle(5, lr, wd=wd)
except RuntimeError:
print(“Your loss function was \n”)
print(learn.loss_func)
raise

This prints out

Your loss function was

<function nll_loss at 0x7f61a0e14ae8>

which is the incorrect loss function, so the loss function was changed somewhere in my code. I will look into it more. Thank you for your help!

1 Like

#19

Any advancement? @clarkeaa13

1 Like

(Antonio de Perio) #20

Any luck here anyone? Running into similar issues

0 Likes