Repository: Lightning-Universe/lightning-flash

Issue:
ImageClassificationData.from_data_frame is not working when predict_data_frame is provided
## 🐛 Bug

When i create a `ImageClassificationData` object with the `from_data_frame` factory method, it works as long as i do not set 
* `predict_images_root`
* `predict_data_frame`

Once i set the above flags i get a **Value-Error**:
```bash
ValueError: File ID `path/to/file.png` did not resolve to an existing file. For use cases which involve first converting the ID to a file you should pass a custom resolver when loading the data.
```

First thing i found is, that the `predict_images_root` is ignored and it is therefore not finding the image.
Digging a little bit digger, it seems that the `.from_data_frame` is not properly calling `ImageClassificationDataFrameInput`:

```python
  train_data = (train_data_frame, input_field, target_fields, train_images_root, train_resolver)
  val_data = (val_data_frame, input_field, target_fields, val_images_root, val_resolver)
  test_data = (test_data_frame, input_field, target_fields, test_images_root, test_resolver)
  predict_data = (predict_data_frame, input_field, predict_images_root, predict_resolver)
```

## Suggestion for fix

The last line should be replaced with

```python
predict_data = (predict_data_frame, input_field, None, predict_images_root, predict_resolver)
```

This way the target_fields are set to `None` and the values are correctly set.

### To Reproduce

Steps to reproduce the behavior:

Just use the `from_data_frame` factory with prediction data, like 
```python
        datamodule = ImageClassificationData.from_data_frame(
            "file", "label",
            train_images_root="/my/root/folder/",
            val_images_root="/my/root/folder/",
            test_images_root="/my/root/folder/",
            train_data_frame=df[df.split == "train"],
            val_data_frame=df[df.split == "valid"],
            test_data_frame=df[df.split == "test"],
            predict_images_root="/my/root/folder/",
            predict_data_frame=df
```

### Expected behavior

see **Suggestion for fix**

### Environment

 - PyTorch Version (e.g., 1.0): 1.10.0
 - Flash Version: 0.6.0
 - OS (e.g., Linux): Ubuntu on Docker
 - How you installed PyTorch (`conda`, `pip`, source): pip with virtualenv
 - Build command you used (if compiling from source):
 - Python version: 3.8.12
 - CUDA/cuDNN version: 11.4
 - GPU models and configuration:
 - Any other relevant information:

ImageClassificationData.from_data_frame is not working when predict_data_frame is provided
## 🐛 Bug

When i create a `ImageClassificationData` object with the `from_data_frame` factory method, it works as long as i do not set 
* `predict_images_root`
* `predict_data_frame`

Once i set the above flags i get a **Value-Error**:
```bash
ValueError: File ID `path/to/file.png` did not resolve to an existing file. For use cases which involve first converting the ID to a file you should pass a custom resolver when loading the data.
```

First thing i found is, that the `predict_images_root` is ignored and it is therefore not finding the image.
Digging a little bit digger, it seems that the `.from_data_frame` is not properly calling `ImageClassificationDataFrameInput`:

```python
  train_data = (train_data_frame, input_field, target_fields, train_images_root, train_resolver)
  val_data = (val_data_frame, input_field, target_fields, val_images_root, val_resolver)
  test_data = (test_data_frame, input_field, target_fields, test_images_root, test_resolver)
  predict_data = (predict_data_frame, input_field, predict_images_root, predict_resolver)
```

## Suggestion for fix

The last line should be replaced with

```python
predict_data = (predict_data_frame, input_field, None, predict_images_root, predict_resolver)
```

This way the target_fields are set to `None` and the values are correctly set.

### To Reproduce

Steps to reproduce the behavior:

Just use the `from_data_frame` factory with prediction data, like 
```python
        datamodule = ImageClassificationData.from_data_frame(
            "file", "label",
            train_images_root="/my/root/folder/",
            val_images_root="/my/root/folder/",
            test_images_root="/my/root/folder/",
            train_data_frame=df[df.split == "train"],
            val_data_frame=df[df.split == "valid"],
            test_data_frame=df[df.split == "test"],
            predict_images_root="/my/root/folder/",
            predict_data_frame=df
```

### Expected behavior

see **Suggestion for fix**

### Environment

 - PyTorch Version (e.g., 1.0): 1.10.0
 - Flash Version: 0.6.0
 - OS (e.g., Linux): Ubuntu on Docker
 - How you installed PyTorch (`conda`, `pip`, source): pip with virtualenv
 - Build command you used (if compiling from source):
 - Python version: 3.8.12
 - CUDA/cuDNN version: 11.4
 - GPU models and configuration:
 - Any other relevant information:



Discussion:
Hey @daMichaelB thanks for reporting this! Your suggested fix looks good to me :smiley: Would you be interested in opening a PR with the fix?
Hello @ethanwharris yes i would love to do that! Give me a little bit of time then i will also add a small test for it, if required?
Awesome, a test would be ideal! No rush :smiley: It looks like we could do with a data frame test in here: https://github.com/PyTorchLightning/lightning-flash/blob/master/tests/image/classification/test_data.py
Hey @daMichaelB thanks for reporting this! Your suggested fix looks good to me :smiley: Would you be interested in opening a PR with the fix?
Hello @ethanwharris yes i would love to do that! Give me a little bit of time then i will also add a small test for it, if required?
Awesome, a test would be ideal! No rush :smiley: It looks like we could do with a data frame test in here: https://github.com/PyTorchLightning/lightning-flash/blob/master/tests/image/classification/test_data.py

Resolving patch:
diff --git a/flash/image/classification/data.py b/flash/image/classification/data.py
--- a/flash/image/classification/data.py
+++ b/flash/image/classification/data.py
@@ -217,7 +217,7 @@ def from_data_frame(
         train_data = (train_data_frame, input_field, target_fields, train_images_root, train_resolver)
         val_data = (val_data_frame, input_field, target_fields, val_images_root, val_resolver)
         test_data = (test_data_frame, input_field, target_fields, test_images_root, test_resolver)
-        predict_data = (predict_data_frame, input_field, predict_images_root, predict_resolver)
+        predict_data = (predict_data_frame, input_field, None, predict_images_root, predict_resolver)
 
         return cls(
             input_cls(RunningStage.TRAINING, *train_data, transform=train_transform, **ds_kw),


Test patch:
diff --git a/tests/image/classification/test_data.py b/tests/image/classification/test_data.py
--- a/tests/image/classification/test_data.py
+++ b/tests/image/classification/test_data.py
@@ -17,6 +17,7 @@
 from typing import Any, List, Tuple
 
 import numpy as np
+import pandas as pd
 import pytest
 import torch
 import torch.nn as nn
@@ -84,6 +85,58 @@ def test_from_filepaths_smoke(tmpdir):
     assert sorted(list(labels.numpy())) == [1, 2]
 
 
+@pytest.mark.skipif(not _IMAGE_TESTING, reason="image libraries aren't installed.")
+def test_from_data_frame_smoke(tmpdir):
+    tmpdir = Path(tmpdir)
+
+    df = pd.DataFrame(
+        {"file": ["train.png", "valid.png", "test.png"], "split": ["train", "valid", "test"], "target": [0, 1, 1]}
+    )
+
+    [_rand_image().save(tmpdir / row.file) for i, row in df.iterrows()]
+
+    img_data = ImageClassificationData.from_data_frame(
+        "file",
+        "target",
+        train_images_root=str(tmpdir),
+        val_images_root=str(tmpdir),
+        test_images_root=str(tmpdir),
+        train_data_frame=df[df.split == "train"],
+        val_data_frame=df[df.split == "valid"],
+        test_data_frame=df[df.split == "test"],
+        predict_images_root=str(tmpdir),
+        batch_size=1,
+        predict_data_frame=df,
+    )
+
+    assert img_data.train_dataloader() is not None
+    assert img_data.val_dataloader() is not None
+    assert img_data.test_dataloader() is not None
+    assert img_data.predict_dataloader() is not None
+
+    data = next(iter(img_data.train_dataloader()))
+    imgs, labels = data["input"], data["target"]
+    assert imgs.shape == (1, 3, 196, 196)
+    assert labels.shape == (1,)
+    assert sorted(list(labels.numpy())) == [0]
+
+    data = next(iter(img_data.val_dataloader()))
+    imgs, labels = data["input"], data["target"]
+    assert imgs.shape == (1, 3, 196, 196)
+    assert labels.shape == (1,)
+    assert sorted(list(labels.numpy())) == [1]
+
+    data = next(iter(img_data.test_dataloader()))
+    imgs, labels = data["input"], data["target"]
+    assert imgs.shape == (1, 3, 196, 196)
+    assert labels.shape == (1,)
+    assert sorted(list(labels.numpy())) == [1]
+
+    data = next(iter(img_data.predict_dataloader()))
+    imgs = data["input"]
+    assert imgs.shape == (1, 3, 196, 196)
+
+
 @pytest.mark.skipif(not _IMAGE_TESTING, reason="image libraries aren't installed.")
 def test_from_filepaths_list_image_paths(tmpdir):
     tmpdir = Path(tmpdir)