Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update readme #4

Closed
wants to merge 80 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
a2caa9e
add fiftyone module availability
ehofesmann May 29, 2021
9e7b38e
add fiftyone datasource
ehofesmann May 29, 2021
2d55c9e
add video classification data source
ehofesmann May 29, 2021
35ef87e
add fiftyone classification serializer
ehofesmann May 29, 2021
2c94a5c
optimizations, rework fo serializer
ehofesmann Jun 2, 2021
c6587ea
support classification, detection, segmentation
ehofesmann Jun 3, 2021
3289ffb
values list, load segmentation dataset in load sample
ehofesmann Jun 3, 2021
3748dbf
FiftyOneLabels test
ehofesmann Jun 3, 2021
7ca3d92
serializer and detection tests
ehofesmann Jun 3, 2021
95690ac
fiftyone classification tests
ehofesmann Jun 3, 2021
5883aae
segmentation and video tests
ehofesmann Jun 3, 2021
ab34980
add detections serializiation test
ehofesmann Jun 3, 2021
036a28b
cleanup
brimoor Jun 3, 2021
1489954
cleanup
brimoor Jun 3, 2021
d4616b1
fix test
ehofesmann Jun 3, 2021
7131cf6
Merge branch 'feature/fiftyone' of github.com:voxel51/lightning-flash…
ehofesmann Jun 3, 2021
51d8046
inherit fiftyonedatasource
ehofesmann Jun 3, 2021
68e1ce3
tweaks
brimoor Jun 3, 2021
6ac7ced
Merge branch 'feature/fiftyone' of https://github.com/voxel51/lightni…
brimoor Jun 3, 2021
78e033a
fix class index
ehofesmann Jun 3, 2021
00cb79d
Merge branch 'feature/fiftyone' of github.com:voxel51/lightning-flash…
ehofesmann Jun 3, 2021
073ce6d
adding helper functions for common operations
brimoor Jun 3, 2021
e57ca24
updating interface
brimoor Jun 3, 2021
4a64b11
always use a Label class
brimoor Jun 4, 2021
42474d8
exposing base class params
brimoor Jun 4, 2021
31229fd
merge
ehofesmann Jun 4, 2021
5b1b09c
indent
ehofesmann Jun 4, 2021
64163b3
revert segmentation optimization
ehofesmann Jun 4, 2021
3763262
revert to mutli
ehofesmann Jun 4, 2021
30f0ec3
linting
brimoor Jun 4, 2021
9b91ea1
adding support for label thresholding
brimoor Jun 4, 2021
09858af
linting
ehofesmann Jun 4, 2021
62d280f
merge
ehofesmann Jun 4, 2021
0561388
Merge branch 'master' into feature/fiftyone
ehofesmann Jun 4, 2021
0ce6ede
update changelog
ehofesmann Jun 4, 2021
5278af2
resolve some issues, clean API
tchaton Jun 4, 2021
f204bb2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 4, 2021
d282d42
normalize detection labels
ehofesmann Jun 7, 2021
aa2de97
normalize detection labels
ehofesmann Jun 7, 2021
d56e5d3
fiftyone serializer return filepaths, paths data sources store filepaths
ehofesmann Jun 9, 2021
55b11c6
formatting
ehofesmann Jun 9, 2021
8e54021
formatting
ehofesmann Jun 9, 2021
1e1022b
remove fiftyone from dir, rename fiftyone_visualize()
ehofesmann Jun 9, 2021
fd94802
merge master
ehofesmann Jun 11, 2021
7c44cb5
update metadata to contain dictionaries
ehofesmann Jun 11, 2021
080d81a
add fiftyone docs
ehofesmann Jun 11, 2021
f34ab3b
update fiftyone examples
ehofesmann Jun 12, 2021
ba21d61
rename from_fiftyone_datasets to from_fiftyone
ehofesmann Jun 14, 2021
9a67c85
fiftyone_visualize to visualize, update docs
ehofesmann Jun 15, 2021
bfd4051
merge master
ehofesmann Jun 15, 2021
49d63bf
resolve comments
ehofesmann Jun 15, 2021
427d280
resolve test issues
ehofesmann Jun 15, 2021
8fe7d76
formatting
ehofesmann Jun 15, 2021
a908b9d
formatting
ehofesmann Jun 15, 2021
895e0bf
yapf formatting
ehofesmann Jun 15, 2021
45dded6
update for current FO version
ehofesmann Jun 15, 2021
60c3947
resolve metadata batch issue
ehofesmann Jun 15, 2021
1525514
Merge remote-tracking branch 'upstream/master' into feature/fiftyone
ehofesmann Jun 15, 2021
5120209
use current FO release, update test requirements
ehofesmann Jun 15, 2021
bec6461
syntax
ehofesmann Jun 15, 2021
6eef60e
update test
ehofesmann Jun 15, 2021
9c9b321
update tests
ehofesmann Jun 15, 2021
6d6aad3
yapf formatting
ehofesmann Jun 15, 2021
691fc7f
one more test...
ehofesmann Jun 15, 2021
8e02feb
update readme
ehofesmann Jun 15, 2021
f896941
update readme
ehofesmann Jun 15, 2021
5945e47
update readme
ehofesmann Jun 15, 2021
21d8042
Merge branch 'master' into feature/fiftyone-readme
brimoor Jun 15, 2021
0aa9e7e
improving visualize() method docs
brimoor Jun 16, 2021
f377416
making docstrings sphinx-friendly
brimoor Jun 16, 2021
7c4defd
docs tweaks
brimoor Jun 16, 2021
ae8dc1f
fiftyone integration docs update
brimoor Jun 16, 2021
8c9295c
simpler
brimoor Jun 16, 2021
59da1cd
using FiftyOne page title
brimoor Jun 16, 2021
66b5f0c
Merge remote-tracking branch 'upstream/master' into feature/fiftyone-…
ehofesmann Jun 16, 2021
f3ce2a4
update embedder example
ehofesmann Jun 16, 2021
5567ba2
isort
ehofesmann Jun 16, 2021
ed1dac0
yapf formatting
ehofesmann Jun 16, 2021
6ffc0eb
fix docstring
ehofesmann Jun 16, 2021
4438bcc
update docstrings
ehofesmann Jun 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,41 @@ classifier = LinearClassifier(128, 10)
When you reach the limits of the flexibility provided by Flash, then seamlessly transition to PyTorch Lightning which
gives you the most flexibility because it is simply organized PyTorch.

## Visualization

Predictions from image and video tasks can be visualized through an [integration with FiftyOne](https://lightning-flash.readthedocs.io/en/latest/integrations/fiftyone.html), allowing you to better understand and analyze how your model is performing.

```python
from flash.core.data.utils import download_data
from flash.core.integrations.fiftyone import visualize
from flash.image import ObjectDetector
from flash.image.detection.serialization import FiftyOneDetectionLabels

# 1. Download the data
# Dataset Credit: https://www.kaggle.com/ultralytics/coco128
download_data(
"https://github.com/zhiqwang/yolov5-rt-stack/releases/download/v0.3.0/coco128.zip",
"data/",
)

# 2. Load the model from a checkpoint and use the FiftyOne serializer
model = ObjectDetector.load_from_checkpoint(
"https://flash-weights.s3.amazonaws.com/object_detection_model.pt"
)
model.serializer = FiftyOneDetectionLabels()

# 3. Detect the object on the images
filepaths = [
"data/coco128/images/train2017/000000000025.jpg",
"data/coco128/images/train2017/000000000520.jpg",
"data/coco128/images/train2017/000000000532.jpg",
]
predictions = model.predict(filepaths)

# 4. Visualize predictions in FiftyOne App
session = visualize(predictions, filepaths=filepaths)
```

## Contribute!
The lightning + Flash team is hard at work building more tasks for common deep-learning use cases. But we're looking for incredible contributors like you to submit new tasks!

Expand Down
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ Lightning Flash

integrations/fiftyone


.. toctree::
:maxdepth: 1
:caption: Contributing a Task
Expand Down
31 changes: 23 additions & 8 deletions docs/source/integrations/fiftyone.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ are supported!
Installation
************

In order to utilize this integration with FiftyOne, you will need to
:ref:`install the tool<fiftyone:installing-fiftyone>`:
In order to utilize this integration, you will need to
:ref:`install FiftyOne <fiftyone:installing-fiftyone>`:

.. code:: shell

pip install fiftyone


*****************************
Visualizing Flash predictions
*****************************
Expand All @@ -52,14 +51,30 @@ one of the following serializers:
The :func:`~flash.core.integrations.fiftyone.visualize` function then lets you visualize
your predictions in the
:ref:`FiftyOne App <fiftyone:fiftyone-app>`. This function accepts a list of
dictionaries containing :ref:`FiftyOne Label<fiftyone:using-labels>` objects
and filepaths which is the exact output of the FiftyOne serializers when the flag
``return_filepath=True`` is specified.
dictionaries containing :ref:`FiftyOne Label <fiftyone:using-labels>` objects
and filepaths, which is exactly the output of the FiftyOne serializers when the
``return_filepath=True`` option is specified.

.. literalinclude:: ../../../flash_examples/integrations/fiftyone/image_classification.py
:language: python
:lines: 14-

The :func:`~flash.core.integrations.fiftyone.visualize` function can be used in
all of the following environments:

- **Local Python shell**: The App will launch in a new tab in your default
web browser
- **Remote Python shell**: Pass the ``remote=True`` option and then follow
the instructions printed to your remote shell to open the App in your
browser on your local machine
- **Jupyter notebook**: The App will launch in the output of your current
cell
- **Google Colab**: The App will launch in the output of your current cell
- **Python script**: Pass the ``wait=True`` option to block execution of your
script until the App is closed

See :ref:`this page <fiftyone:environments>` for more information about
using the FiftyOne App in different environments.

***********************
Using FiftyOne datasets
Expand All @@ -73,15 +88,14 @@ ground truth annotations. This allows you to perform more complex analysis with

The
:meth:`~flash.core.data.data_module.DataModule.from_fiftyone`
method allows you to load your FiftyOne Datasets directly into a
method allows you to load your FiftyOne datasets directly into a
:class:`~flash.core.data.data_module.DataModule` to be used for training,
testing, or inference.

.. literalinclude:: ../../../flash_examples/integrations/fiftyone/image_classification_fiftyone_datasets.py
:language: python
:lines: 14-


**********************
Visualizing embeddings
**********************
Expand Down Expand Up @@ -130,6 +144,7 @@ FiftyOneSegmentationLabels

.. autoclass:: flash.image.segmentation.serialization.FiftyOneSegmentationLabels
:members:
:noindex:

.. _fiftyone_detection_labels:

Expand Down
67 changes: 44 additions & 23 deletions flash/core/integrations/fiftyone/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,72 @@


def visualize(
labels: Union[List[Label], List[Dict[str, Label]]],
predictions: Union[List[Label], List[Dict[str, Label]]],
filepaths: Optional[List[str]] = None,
wait: Optional[bool] = True,
label_field: Optional[str] = "predictions",
wait: Optional[bool] = False,
**kwargs
) -> Optional[Session]:
"""Use the result of a FiftyOne serializer to visualize predictions in the
FiftyOne App.
"""Visualizes predictions from a model with a FiftyOne Serializer in the
:ref:`FiftyOne App <fiftyone:fiftyone-app>`.

This method can be used in all of the following environments:

- **Local Python shell**: The App will launch in a new tab in your
default web browser.
- **Remote Python shell**: Pass the ``remote=True`` option to this method
and then follow the instructions printed to your remote shell to open
the App in your browser on your local machine.
- **Jupyter notebook**: The App will launch in the output of your current
cell.
- **Google Colab**: The App will launch in the output of your current
cell.
- **Python script**: Pass the ``wait=True`` option to block execution of
your script until the App is closed.

See :ref:`this page <fiftyone:environments>` for more information about
using the FiftyOne App in different environments.

Args:
labels: Either a list of FiftyOne labels that will be applied to the
corresponding filepaths provided with through `filepath` or
`datamodule`. Or a list of dictionaries containing image/video
filepaths and corresponding FiftyOne labels.
predictions: Can be either a list of FiftyOne labels that will be
matched with the corresponding ``filepaths``, or a list of
dictionaries with "filepath" and "predictions" keys that contains
the filepaths and predictions.
filepaths: A list of filepaths to images or videos corresponding to the
provided `labels`.
wait: A boolean determining whether to launch the FiftyOne session and
wait until the session is closed or whether to return immediately.
label_field: The string of the label field in the FiftyOne dataset
containing predictions
provided ``predictions``.
label_field: The name of the label field in which to store the
predictions in the FiftyOne dataset.
wait: Whether to block execution until the FiftyOne App is closed.
**kwargs: Optional keyword arguments for
:meth:`fiftyone:fiftyone.core.session.launch_app`.

Returns:
a :class:`fiftyone:fiftyone.core.session.Session`
"""
if not _FIFTYONE_AVAILABLE:
raise ModuleNotFoundError("Please, `pip install fiftyone`.")
if flash._IS_TESTING:
return None

# Flatten list if batches were used
if all(isinstance(fl, list) for fl in labels):
labels = list(chain.from_iterable(labels))
if all(isinstance(fl, list) for fl in predictions):
predictions = list(chain.from_iterable(predictions))

if all(isinstance(fl, dict) for fl in labels):
filepaths = [lab["filepath"] for lab in labels]
labels = [lab["predictions"] for lab in labels]
if all(isinstance(fl, dict) for fl in predictions):
filepaths = [lab["filepath"] for lab in predictions]
labels = [lab["predictions"] for lab in predictions]
else:
labels = predictions

if filepaths is None:
raise ValueError("The `filepaths` argument is required if filepaths are not provided in `labels`.")

dataset = fo.Dataset()
if filepaths:
dataset.add_labeled_images(
list(zip(filepaths, labels)),
LabeledImageTupleSampleParser(),
label_field=label_field,
)
dataset.add_samples([fo.Sample(filepath=f, **{label_field: l}) for f, l in zip(filepaths, labels)])

session = fo.launch_app(dataset, **kwargs)
if wait:
session.wait()

return session
34 changes: 18 additions & 16 deletions flash/image/segmentation/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@


class SegmentationLabels(Serializer):
"""A :class:`.Serializer` which converts the model outputs to the label of
the argmax classification per pixel in the image for semantic segmentation
tasks.

def __init__(self, labels_map: Optional[Dict[int, Tuple[int, int, int]]] = None, visualize: bool = False):
"""A :class:`.Serializer` which converts the model outputs to the label of the argmax classification
per pixel in the image for semantic segmentation tasks.
Args:
labels_map: A dictionary that map the labels ids to pixel intensities.
visualize: Wether to visualize the image labels.
"""

Args:
labels_map: A dictionary that map the labels ids to pixel intensities.
visualize: Wether to visualize the image labels.
"""
def __init__(self, labels_map: Optional[Dict[int, Tuple[int, int, int]]] = None, visualize: bool = False):
super().__init__()
self.labels_map = labels_map
self.visualize = visualize
Expand Down Expand Up @@ -89,22 +90,23 @@ def serialize(self, sample: Dict[str, torch.Tensor]) -> torch.Tensor:


class FiftyOneSegmentationLabels(SegmentationLabels):
"""A :class:`.Serializer` which converts the model outputs to FiftyOne
segmentation format.

Args:
labels_map: A dictionary that map the labels ids to pixel intensities.
visualize: whether to visualize the image labels.
return_filepath: Boolean determining whether to return a dict
containing filepath and FiftyOne labels (True) or only a list of
FiftyOne labels (False).
"""

def __init__(
self,
labels_map: Optional[Dict[int, Tuple[int, int, int]]] = None,
visualize: bool = False,
return_filepath: bool = False,
):
"""A :class:`.Serializer` which converts the model outputs to FiftyOne segmentation format.

Args:
labels_map: A dictionary that map the labels ids to pixel intensities.
visualize: Wether to visualize the image labels.
return_filepath: Boolean determining whether to return a dict
containing filepath and FiftyOne labels (True) or only a
list of FiftyOne labels (False)
"""
if not _FIFTYONE_AVAILABLE:
raise ModuleNotFoundError("Please, run `pip install fiftyone`.")

Expand Down
7 changes: 3 additions & 4 deletions flash_examples/integrations/fiftyone/image_classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@

# 4 Predict from checkpoint
model = ImageClassifier.load_from_checkpoint("https://flash-weights.s3.amazonaws.com/image_classification_model.pt")
model.serializer = FiftyOneLabels(return_filepath=True)
model.serializer = FiftyOneLabels(return_filepath=True) # output FiftyOne format
predictions = trainer.predict(model, datamodule=datamodule)

predictions = list(chain.from_iterable(predictions)) # flatten batches

# 5. Visualize predictions in FiftyOne
# Note: this blocks until the FiftyOne App is closed
# 5 Visualize predictions in FiftyOne App
# Optional: pass `wait=True` to block execution until App is closed
session = visualize(predictions)
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
dataset_type=fo.types.ImageClassificationDirectoryTree,
)

# 3 Load FiftyOne datasets
# 3 Load data into Flash
datamodule = ImageClassificationData.from_fiftyone(
train_dataset=train_dataset,
val_dataset=val_dataset,
test_dataset=test_dataset,
)

# 4 Fine tune a model
# 4 Fine tune model
model = ImageClassifier(
backbone="resnet18",
num_classes=datamodule.num_classes,
Expand All @@ -66,28 +66,22 @@

# 5 Predict from checkpoint on data with ground truth
model = ImageClassifier.load_from_checkpoint("https://flash-weights.s3.amazonaws.com/image_classification_model.pt")
model.serializer = FiftyOneLabels(return_filepath=False)
model.serializer = FiftyOneLabels(return_filepath=False) # output FiftyOne format
datamodule = ImageClassificationData.from_fiftyone(predict_dataset=test_dataset)
predictions = trainer.predict(model, datamodule=datamodule)

predictions = list(chain.from_iterable(predictions)) # flatten batches

# 6 Add predictions to dataset
test_dataset.set_values("predictions", predictions)

# 7 Visualize labels in the App
session = fo.launch_app(test_dataset)

# 8 Evaluate your model
results = test_dataset.evaluate_classifications(
"predictions",
gt_field="ground_truth",
eval_key="eval",
)
# 7 Evaluate your model
results = test_dataset.evaluate_classifications("predictions", gt_field="ground_truth", eval_key="eval")
results.print_report()
plot = results.plot_confusion_matrix()
plot.show()

# Only when running this in a script
# Block until the FiftyOne App is closed
# 8 Visualize results in the App
session = fo.launch_app(test_dataset)

# Optional: block execution until App is closed
session.wait()
7 changes: 2 additions & 5 deletions flash_examples/integrations/fiftyone/image_embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,17 @@
)

# 3 Load model
embedder = ImageEmbedder(backbone="swav-imagenet", embedding_dim=128)
embedder = ImageEmbedder(backbone="resnet101", embedding_dim=128)

# 4 Generate embeddings
filepaths = dataset.values("filepath")
embeddings = np.stack(embedder.predict(filepaths))

# 5 Visualize in FiftyOne App
results = fob.compute_visualization(dataset, embeddings=embeddings)

session = fo.launch_app(dataset)

plot = results.visualize(labels="ground_truth.label")
plot.show()

# Only when running this in a script
# Block until the FiftyOne App is closed
# Optional: block execution until App is closed
session.wait()