diff --git a/docs/source/_static/images/integrations/lightning_flash.png b/docs/source/_static/images/integrations/lightning_flash.png new file mode 100644 index 0000000000..ff8eb2165b Binary files /dev/null and b/docs/source/_static/images/integrations/lightning_flash.png differ diff --git a/docs/source/_static/images/integrations/open_images.png b/docs/source/_static/images/integrations/open_images.png new file mode 100644 index 0000000000..099b3870fa Binary files /dev/null and b/docs/source/_static/images/integrations/open_images.png differ diff --git a/docs/source/integrations/index.rst b/docs/source/integrations/index.rst index a6d688d440..dc18027202 100644 --- a/docs/source/integrations/index.rst +++ b/docs/source/integrations/index.rst @@ -5,9 +5,8 @@ FiftyOne Integrations .. default-role:: code -FiftyOne is designed to fit into your existing workflows as easily as possible. -To this end, FiftyOne has integrated with various existing datasets, tools, and -services that you are likely using. +FiftyOne integrates naturally with other ML tools that you know and love. Click +on the cards below to see how! .. Integrations cards section ----------------------------------------------------- @@ -35,38 +34,46 @@ services that you are likely using. .. Add tutorial cards below .. customcarditem:: - :header: PyTorch Lightning Flash - :description: Flash now supports FiftyOne datasets letting you train and evaluate Flash tasks on your FiftyOne datasets in minimal lines of code. + :header: Lightning Flash + :description: Train Flash models on FiftyOne datasets and use the FiftyOne App to visualize and improve your Flash models, all with just a few lines of code. :link: lightning_flash.html - :image: https://raw.githubusercontent.com/PyTorchLightning/lightning-flash/master/docs/source/_static/images/logo.svg + :image: ../_static/images/integrations/lightning_flash.png :tags: Model-Training,Model-Evaluation -.. +.. Upcoming integrations + .. customcarditem:: - :header: Google's Open Images Dataset - :description: FiftyOne is the easiest source for downloading and exploring Open Images. + :header: Open Images Dataset + :description: FiftyOne is the recommended tool for downloading and exploring Google's Open Images Dataset. :link: ../user_guide/dataset_zoo/datasets.html#dataset-zoo-open-images-v6 + :image: ../_static/images/integrations/open_images.png :tags: Dataset - + .. customcarditem:: - :header: CVAT - :description: Import and export image and video datasets in CVAT format. - :link: ../user_guide/dataset_creation/datasets.html#cvatimagedataset - :tags: Annotation - + :header: COCO Dataset + :description: FiftyOne is the recommended tool for downloading and exploring the COCO Dataset. + :link: ../user_guide/dataset_zoo/datasets.html#dataset-zoo-coco-2017 + :tags: Dataset + .. customcarditem:: - :header: Labelbox - :description: Download and upload your data directly to Labelbox. - :link: ../api/fiftyone.utils.labelbox.html + :header: CVAT + :description: Use our CVAT integration to easily annotate and edit your FiftyOne datasets. + :link: ../user_guide/dataset_creation/datasets.html#cvatimagedataset + :tags: Annotation + + .. customcarditem:: + :header: Labelbox + :description: Use our Labelbox integration to easily annotate and edit your FiftyOne datasets. + :link: ../api/fiftyone.utils.labelbox.html :image: https://voxel51.com/images/integrations/labelbox-128.png - :tags: Annotation - + :tags: Annotation + .. customcarditem:: - :header: Scale AI - :description: Import and export labels to and from the Scale AI format. - :link: ../api/fiftyone.utils.scale.html + :header: Scale AI + :description: Use our Scale integration to easily annotate and edit your FiftyOne datasets. + :link: ../api/fiftyone.utils.scale.html :image: https://voxel51.com/images/integrations/scale-128.png - :tags: Annotation + :tags: Annotation .. End of integrations cards @@ -86,4 +93,4 @@ services that you are likely using. :maxdepth: 1 :hidden: - PyTorch Lightning Flash + Lightning Flash diff --git a/docs/source/integrations/lightning_flash.rst b/docs/source/integrations/lightning_flash.rst index aaf8fdcdb5..e46bfc2eee 100644 --- a/docs/source/integrations/lightning_flash.rst +++ b/docs/source/integrations/lightning_flash.rst @@ -1,221 +1,222 @@ -.. _flash: +.. _lightning-flash: -PyTorch Lightning Flash Integration -=================================== +Lightning Flash Integration +=========================== .. default-role:: code -We've collaborated with the -`PyTorch Lightning Flash `_ -team to make it easy to train :class:`Flash tasks ` -on your :ref:`FiftyOne datasets ` and add predictions from Flash -models to your datasets for visualization and analysis, all -in just a few lines of code. +We've collaborated with `Grid AI `_, the team behind the +amazing `PyTorch Lightning `_ +and `Lightning Flash `_ +projects, to make it easy to train +:class:`Flash tasks ` on your +:ref:`FiftyOne datasets ` and add predictions from your Flash +models to your FiftyOne datasets for visualization and analysis, all in just a +few lines of code! The following Flash tasks are supported natively by FiftyOne: -- :ref:`Image Classification ` -- :ref:`Image Object Detection ` -- :ref:`Image Semantic Segmentation ` -- :ref:`Image Embedding ` -- :ref:`Video Classification ` +- :ref:`Image classification ` +- :ref:`Object detection ` +- :ref:`Semantic segmentation ` +- :ref:`Image embeddings ` +- :ref:`Video classification ` -Support for future Flash tasks is on the horizon. +.. note:: + + As Lightning Flash adds support for additional computer vision tasks, we'll + roll out native support for them in FiftyOne via this integration! -.. _install-flash: +.. _flash-install: Setup _____ -In order to utilize PyTorch Lightning Flash, you need to -`install the tool `_: +In order to use the Lightning Flash integration, you'll need to +`install it `_: .. code-block:: shell pip install lightning-flash - .. _flash-model-training: Model training ______________ -You can easily train or finetune a Flash -:class:`Task` -on your +You can easily train or finetune a Flash +:class:`Task` on your :ref:`FiftyOne datasets ` with just a few lines of code using -Flash's builtin +Flash's builtin :meth:`DataModule.from_fiftyone() ` -method, which is implemented for each of the Flash tasks shown below: +method, which is implemented for each of the Flash tasks shown below. .. tabs:: .. tab:: Image classification - This example trains a Flash image classification task on a FiftyOne dataset - with |Classifications| ground truth labels. - + The example below finetunes a Flash image classification task on a + FiftyOne dataset with |Classification| ground truth labels: + .. code-block:: python :linenos: from itertools import chain - - import fiftyone as fo - import fiftyone.zoo as foz - + from flash import Trainer from flash.core.classification import FiftyOneLabels from flash.core.finetuning import FreezeUnfreeze from flash.image import ImageClassificationData, ImageClassifier - + + import fiftyone as fo + import fiftyone.zoo as foz + # 1. Load your FiftyOne dataset - # Here we use views into one dataset, but you can also create a - # different dataset for each split - dataset = foz.load_zoo_dataset("cifar10", split="test", max_samples=40) - train_dataset = dataset.shuffle(seed=51)[:20] - test_dataset = dataset.shuffle(seed=51)[20:25] - val_dataset = dataset.shuffle(seed=51)[25:30] - predict_dataset = dataset.shuffle(seed=51)[30:40] - + + dataset = foz.load_zoo_dataset("cifar10", split="test", max_samples=40, shuffle=True) + + # Here we use views into one dataset, but you can also use a different dataset + # for each split + train_dataset = dataset[:20] + test_dataset = dataset[20:25] + val_dataset = dataset[25:30] + predict_dataset = dataset[30:40] + # 2. Load the Datamodule datamodule = ImageClassificationData.from_fiftyone( - train_dataset = train_dataset, - test_dataset = test_dataset, - val_dataset = val_dataset, - predict_dataset = predict_dataset, - label_field = "ground_truth", + train_dataset=train_dataset, + test_dataset=test_dataset, + val_dataset=val_dataset, + predict_dataset=predict_dataset, + label_field="ground_truth", batch_size=4, num_workers=4, ) - + # 3. Build the model model = ImageClassifier( - backbone="resnet18", - num_classes=datamodule.num_classes, + backbone="resnet18", + num_classes=datamodule.num_classes, serializer=FiftyOneLabels(), ) - + # 4. Create the trainer - trainer = Trainer( - max_epochs=1, - limit_train_batches=1, - limit_val_batches=1, - ) - + trainer = Trainer(max_epochs=1, limit_train_batches=1, limit_val_batches=1) + # 5. Finetune the model trainer.finetune( - model, + model, datamodule=datamodule, strategy=FreezeUnfreeze(unfreeze_epoch=1), ) - + # 6. Save it! trainer.save_checkpoint("image_classification_model.pt") - + # 7. Generate predictions + model = ImageClassifier.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/image_classification_model.pt" ) model.serializer = FiftyOneLabels() predictions = trainer.predict(model, datamodule=datamodule) - predictions = list(chain.from_iterable(predictions)) # flatten batches - - # 8. Add predictions to dataset and analyze + + # Add predictions to FiftyOne dataset predict_dataset.set_values("flash_predictions", predictions) + + # 8. Analyze predictions in the App session = fo.launch_app(view=predict_dataset) - - .. tab:: Image object detection + .. tab:: Object detection + + This example below finetunes a Flash object detection task on a + FiftyOne dataset with |Detections| ground truth labels: - This example trains a Flash object detection task on a FiftyOne dataset - with |Detections| ground truth labels. - .. code-block:: python :linenos: from itertools import chain - - import fiftyone as fo - import fiftyone.zoo as foz - + from flash import Trainer from flash.image import ObjectDetectionData, ObjectDetector from flash.image.detection.serialization import FiftyOneDetectionLabels - + + import fiftyone as fo + import fiftyone.zoo as foz + # 1. Load your FiftyOne dataset - # Here we use views into one dataset, but you can also create a - # different dataset for each split - dataset = foz.load_zoo_dataset("quickstart", max_samples=40) - train_dataset = dataset.shuffle(seed=51)[:20] - test_dataset = dataset.shuffle(seed=51)[20:25] - val_dataset = dataset.shuffle(seed=51)[25:30] - predict_dataset = dataset.shuffle(seed=51)[30:40] - + + dataset = foz.load_zoo_dataset("quickstart", max_samples=40, shuffle=True) + + # Here we use views into one dataset, but you can also use a different dataset + # for each split + train_dataset = dataset[:20] + test_dataset = dataset[20:25] + val_dataset = dataset[25:30] + predict_dataset = dataset[30:40] + # 2. Load the Datamodule datamodule = ObjectDetectionData.from_fiftyone( - train_dataset = train_dataset, - test_dataset = test_dataset, - val_dataset = val_dataset, - predict_dataset = predict_dataset, - label_field = "ground_truth", + train_dataset=train_dataset, + test_dataset=test_dataset, + val_dataset=val_dataset, + predict_dataset=predict_dataset, + label_field="ground_truth", batch_size=4, num_workers=4, ) - + # 3. Build the model model = ObjectDetector( - model="retinanet", + model="retinanet", num_classes=datamodule.num_classes, serializer=FiftyOneDetectionLabels(), ) - + # 4. Create the trainer - trainer = Trainer( - max_epochs=1, - limit_train_batches=1, - limit_val_batches=1, - ) - + trainer = Trainer(max_epochs=1, limit_train_batches=1, limit_val_batches=1) + # 5. Finetune the model trainer.finetune(model, datamodule=datamodule) - + # 6. Save it! trainer.save_checkpoint("object_detection_model.pt") - + # 7. Generate predictions + model = ObjectDetector.load_from_checkpoint( - "https://flash-weights.s3.amazonaws.com/object_detection_model.pt" + "https://flash-weights.s3.amazonaws.com/object_detection_model.pt" ) model.serializer = FiftyOneDetectionLabels() predictions = trainer.predict(model, datamodule=datamodule) - predictions = list(chain.from_iterable(predictions)) # flatten batches - - # 8. Add predictions to dataset and analyze + + # Add predictions to FiftyOne dataset predict_dataset.set_values("flash_predictions", predictions) + + # 8. Analyze predictions in the App session = fo.launch_app(view=predict_dataset) + .. tab:: Semantic segmentation - .. tab:: Image semantic segmentation + This example below finetunes a Flash semantic segmentation task on a + FiftyOne dataset with |Segmentation| ground truth labels: - This example trains a Flash semantic segmentation task on a FiftyOne dataset - with |Segmentation| ground truth labels. - .. code-block:: python :linenos: - + from itertools import chain - import fiftyone as fo - import fiftyone.zoo as foz - from flash import Trainer from flash.core.data.utils import download_data from flash.image import SemanticSegmentation, SemanticSegmentationData - from flash.image.segmentation.serialization import FiftyOneSegmentationLabels + from flash.image.segmentation.serialization import FiftyOneSegmentationLabels + + import fiftyone as fo + import fiftyone.zoo as foz # 1. Load your FiftyOne dataset # This is a Dataset with Semantic Segmentation Labels generated via CARLA @@ -223,120 +224,123 @@ method, which is implemented for each of the Flash tasks shown below: # The data was generated as part of the Lyft Udacity Challenge. # More info here: https://www.kaggle.com/kumaresanmanickavelu/lyft-udacity-challenge + download_data( - "https://github.com/ongchinkiat/LyftPerceptionChallenge/releases/download/v0.1/carla-capture-20180513A.zip", - "data/" + "https://github.com/ongchinkiat/LyftPerceptionChallenge/releases/download/v0.1/carla-capture-20180513A.zip", + "data/" ) - # Here we use views into one dataset, but you can also create a - # different dataset for each split dataset = fo.Dataset.from_dir( - dataset_dir = "data", - data_path = "CameraRGB", - labels_path = "CameraSeg", - max_samples = 40, - force_grayscale = True, + dataset_dir="data", dataset_type=fo.types.ImageSegmentationDirectory, + data_path="CameraRGB", + labels_path="CameraSeg", + force_grayscale=True, + max_samples=40, + shuffle=True, ) - train_dataset = dataset.shuffle(seed=51)[:20] - test_dataset = dataset.shuffle(seed=51)[20:25] - val_dataset = dataset.shuffle(seed=51)[25:30] - predict_dataset = dataset.shuffle(seed=51)[30:40] - + + # Here we use views into one dataset, but you can also create a + # different dataset for each split + train_dataset = dataset[:20] + test_dataset = dataset[20:25] + val_dataset = dataset[25:30] + predict_dataset = dataset[30:40] + # 2. Load the Datamodule datamodule = SemanticSegmentationData.from_fiftyone( - train_dataset = train_dataset, - test_dataset = test_dataset, - val_dataset = val_dataset, - predict_dataset = predict_dataset, - label_field = "ground_truth", + train_dataset=train_dataset, + test_dataset=test_dataset, + val_dataset=val_dataset, + predict_dataset=predict_dataset, + label_field="ground_truth", batch_size=4, num_workers=4, ) - + # 3. Build the model model = SemanticSegmentation( - backbone="fcn_resnet50", + backbone="fcn_resnet50", num_classes=datamodule.num_classes, serializer=FiftyOneSegmentationLabels(), ) - + # 4. Create the trainer - trainer = Trainer( - max_epochs=1, - fast_dev_run=1, - ) - + trainer = Trainer(max_epochs=1, fast_dev_run=1) + # 5. Finetune the model trainer.finetune(model, datamodule=datamodule, strategy="freeze") - + # 6. Save it! trainer.save_checkpoint("semantic_segmentation_model.pt") - + # 7. Generate predictions + model = ObjectDetector.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/semantic_segmentation_model.pt" ) model.serializer = FiftyOneSegmentationLabels() predictions = trainer.predict(model, datamodule=datamodule) - predictions = list(chain.from_iterable(predictions)) # flatten batches - - # 8. Add predictions to dataset and analyze + + # Add predictions to FiftyOne dataset predict_dataset.set_values("flash_predictions", predictions) - session = fo.launch_app(view=predict_dataset) + # 8. Analyze predictions in the App + session = fo.launch_app(view=predict_dataset) .. tab:: Video classification - This example trains a Flash video classification task on a FiftyOne dataset - with |Classifications| ground truth labels. - + The example below finetunes a Flash video classification task on a + FiftyOne dataset with |Classification| ground truth labels: + .. code-block:: python :linenos: from torch.utils.data.sampler import RandomSampler - + import flash from flash.core.classification import FiftyOneLabels from flash.core.data.utils import download_data from flash.video import VideoClassificationData, VideoClassifier - + import fiftyone as fo - + # 1. Download data download_data("https://pl-flash-data.s3.amazonaws.com/kinetics.zip") - + # 2. Load data into FiftyOne - # Here we use different datasets for each split, but you can also - # use views into the same dataset + # Here we use different datasets for each split, but you can also use views + # into the same dataset + train_dataset = fo.Dataset.from_dir( "data/kinetics/train", fo.types.VideoClassificationDirectoryTree, label_field="ground_truth", max_samples=5, ) - + val_dataset = fo.Dataset.from_dir( "data/kinetics/val", fo.types.VideoClassificationDirectoryTree, label_field="ground_truth", max_samples=5, ) - + predict_dataset = fo.Dataset.from_dir( "data/kinetics/predict", fo.types.VideoDirectory, max_samples=5, ) - + # 3. Finetune a model + classifier = VideoClassifier.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/video_classification.pt", pretrained=False, ) - + datamodule = VideoClassificationData.from_fiftyone( train_dataset=train_dataset, val_dataset=val_dataset, @@ -349,99 +353,101 @@ method, which is implemented for each of the Flash tasks shown below: decode_audio=False, num_workers=8, ) - + trainer = flash.Trainer(max_epochs=1, fast_dev_run=1) trainer.finetune(classifier, datamodule=datamodule) trainer.save_checkpoint("video_classification.pt") - + # 4. Predict from checkpoint + classifier = VideoClassifier.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/video_classification.pt", pretrained=False, ) - classifier.serializer = FiftyOneLabels() - + filepaths = predict_dataset.values("filepath") predictions = classifier.predict(filepaths) - + + # Add predictions to FiftyOne dataset predict_dataset.set_values("predictions", predictions) - + # 5. Visualize in FiftyOne App session = fo.launch_app(predict_dataset) +.. _flash-model-predictions: -.. _adding-model-predictions: - -Adding model predictions -________________________ - -Once you have a trained Flash task, there are a couple of ways that -you can use the FiftyOne integrations to -add generate and add model predictions to your |Dataset| or |DatasetView|. - +Model predictions +_________________ -Apply model ------------ +Once you have a trained Flash task, you can add model predictions to a FiftyOne +|Dataset| or |DatasetView| in just a few lines of code using either of the +patterns below. -The easiest way to generate predictions on an existing |Dataset| or |DatasetView| is -to use the :meth:`apply_model() ` -function, passing in your Flash model. Behind the scenes, FiftyOne will -construct the appropriate Flash -:class:`Trainer() ` and use it to perform -inference to generate the predictions. These -:class:`Trainers ` support distributed and -parallel inference, so you can pass additional arguments like `num_gpus=32` -into :meth:`apply_model() ` -and they will be passed along when constructing the -:class:`Trainer `. +Applying Flash models to FiftyOne datasets +------------------------------------------ +The easiest way to generate predictions on a FiftyOne |Dataset| or +|DatasetView| with a Flash model is to use the +builtin :meth:`apply_model() ` +function, which natively accepts Flash models of any +:ref:`supported type `. +Behind the scenes, FiftyOne will construct the appropriate Flash +:class:`Trainer ` and FiftyOne-style +:class:`Serializer ` to perform the +inference and output the predictions as FiftyOne |Label| instances that are +added to your dataset. .. code-block:: python :linenos: + from flash.image import ObjectDetector + import fiftyone as fo import fiftyone.zoo as foz - from flash.image import ObjectDetector - # Load your dataset dataset = foz.load_zoo_dataset("quickstart", max_samples=5) - # Load the finetuned model + # Load your Flash model model = ObjectDetector.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/object_detection_model.pt" ) - # Predict + # Predict! dataset.apply_model(model, label_field="flash_predictions") # Visualize session = fo.launch_app(dataset) +.. note:: + + When performing inference with Flash models, you can pass additional + arguments like ``num_gpus=8`` to + :meth:`apply_model() `, + which are used to initialize the Flash + :class:`Trainer ` to configure + distributed and/or parallelized inference per your needs! Manually adding predictions --------------------------- -In some cases, you may have loaded your FiftyOne dataset into a Flash -:class:`DataModule ` -already and want to generate predictions with those. - -Flash models support different -:class:`serializers ` -, objects that reformat the output of -models. Using -:ref:`FiftyOne serializers `, you can return predictions as FiftyOne -|Label| objects directly. All you need to do is set the model serializer to the -corresponding FiftyOne serializer for your task and generate predictions. -FiftyOne serializers also support a :class:`return_filepath ` -flag that will return the corresponding filepath of every sample along -with the |Label| objects. +If you've already loaded your datasets into Flash +:class:`DataModules ` without +using FiftyOne, you can still easily use FiftyOne to analyze your model's +predictions by swapping out your model's default +:class:`Serializer ` for the +:ref:`FiftyOne-style serializer ` of the appropriate +type. + +Flash models with FiftyOne serializers will directly return predictions as +|Label| objects that you can easily add to your FiftyOne datasets via +:meth:`set_values `. .. code-block:: python :linenos: - + from itertools import chain import fiftyone as fo @@ -458,15 +464,12 @@ with the |Label| objects. model = ObjectDetector.load_from_checkpoint( "https://flash-weights.s3.amazonaws.com/object_detection_model.pt" ) - model.serializer = FiftyOneDetectionLabels() + model.serializer = FiftyOneDetectionLabels() - # Option 1: Predict with trainer (Supports distributed inference) - datamodule = ObjectDetectionData.from_fiftyone( - predict_dataset=dataset, - ) - trainer = Trainer() + # Option 1: Predict with trainer (supports distributed inference) + datamodule = ObjectDetectionData.from_fiftyone(predict_dataset=dataset) + trainer = Trainer() predictions = trainer.predict(model, datamodule=datamodule) - predictions = list(chain.from_iterable(predictions)) # flatten batches # Option 2: Predict with model @@ -476,76 +479,70 @@ with the |Label| objects. # Add predictions to dataset dataset.set_values("flash_predictions", predictions) - # Visualize + # Visualize in the App session = fo.launch_app(dataset) +.. note:: -Image Embeddings -________________ + FiftyOne serializers support an optional + :class:`return_filepath ` + flag that supports returning dicts that contain both the |Label| objects + and the ``filepath`` of the associated media. -Lightning Flash has a -:ref:`task that generates image embeddings ` -in the form of a vector of image features. When used together with the -:ref:`dimensionality reduction` and -:ref:`interactive plotting` -capabilities in the FiftyOne, you can accomplish -powerful workflows like clustering, similarity search, pre-annotation, and more -in only a few lines of code. +.. _flash-image-embeddings: +Image embeddings +________________ + +If you use Lightning Flash's +:ref:`image embeddings tasks ` to generate feature +vectors for your image datasets, then use can easily leverage FiftyOne's +:ref:`dimensionality reduction ` and +:ref:`interactive plotting ` capabilities to visualize your +Flash model's embeddings and execute powerful workflows like +:doc:`cluster analysis <../tutorials/image_embeddings>` and +:ref:`similarity search `, all in only a few lines of code! .. code-block:: python :linenos: import numpy as np import torch - + from flash.core.data.utils import download_data from flash.image import ImageEmbedder - + import fiftyone as fo import fiftyone.brain as fob - + # 1 Download data download_data( "https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip" ) - + # 2 Load data into FiftyOne dataset = fo.Dataset.from_dir( "data/hymenoptera_data/test/", fo.types.ImageClassificationDirectoryTree, ) - + # 3 Load model embedder = ImageEmbedder(backbone="swav-imagenet", 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) - + + # 5 Visualize images session = fo.launch_app(dataset) - + + # 6 Visualize image embeddings + + results = fob.compute_visualization(dataset, embeddings=embeddings) + plot = results.visualize(labels="ground_truth.label") plot.show() - .. image:: ../images/integrations/flash_embeddings.png :alt: embeddings_example :align: center - - -.. note:: - - Interactive plots are currently only supported in Jupyter notebooks. In the - meantime, you can still use FiftyOne's plotting features in other - environments, but you must manually call - :meth:`plot.show() ` to update the - state of a plot to match the state of a connected |Session|, and any - callbacks that would normally be triggered in response to interacting with - a plot will not be triggered. - - See :ref:`this section ` for more information. -