Effortless Task Inspection with Default Cards
Metaflow comes with a built-in Default Card that shows all artifacts produced by a task. Let’s create a simple flow to test it.
from metaflow import FlowSpec, Parameter, step, card
from datetime import datetime
class DefaultCardFlow(FlowSpec):
alpha = Parameter('alpha', default=0.5)
@card
@step
def start(self):
self.example_dict = {'first_key': list(range(10)),
'second_key': {'one', 'two'}}
self.timestamp = datetime.utcnow()
self.next(self.end)
@step
def end(self):
pass
if __name__ == "__main__":
DefaultCardFlow()
The only new feature introduced in this flow is the @card
decorator which attaches a
card to the start
step. Since no arguments are given to the decorator, the Default
Card is used. Save the example in defaultcard.py
and execute the flow as usual:
python defaultcard.py run
After the run has finished, you can open a generated card on the command line:
python defaultcard.py card view start
The command will open the card in your web browser. It will look like this:
The Default Card shows basic metadata about the task, parameters given for the flow, artifacts accessible in the task, as well as a visualization of the flow DAG. You can use this information to quickly observe and verify results of a task without making any changes in the code.
Using Local Card Viewer
Local Card Viewer was introduced in Metaflow 2.11. Make sure you have a recent enough version of Metaflow to use this feature.
Opening a card manually after each run with card view
can get tedious. If you
have access to Metaflow UI, you can view cards automatically in the UI. If you
don't have the UI deployed, Metaflow provides a simple built-in viewer, Local
Card Viewer, which sets up a local server for viewing cards.
Open a new terminal, navigate to the same working directory where you will be executing flows, and run this command
python defaultcard.py card server
In your browser, go to localhost:8324. You should see the latest card created with a header that allows you to navigate through all cards produced by the latest run:
If you run the flow again
python defaultcard.py run
the viewer updates automatically to show the latest card. This way, you can keep the viewer
open on the side, as you develop your flows in another window. Importantly,
the local viewer allows you to view updating cards in real-time,
similar to Metaflow UI, while the card view
command only shows a card that was
available at the time when you executed the command.
Visualizing Artifacts with the Default Card
As shown in the screenshot above, the artifacts table shows all Metaflow artifacts related to the task. Large artifacts are truncated for display - you can access the originals using the Client API.
If an artifact contains an image or a dataframe, the artifact is visualized in a
separate section in addition to its string representation. Take a look at the following
example which contains an artifact, self.image
storing an animated GIF and another
artifact, a Pandas dataframe
:
from metaflow import FlowSpec, Parameter, step
import requests, pandas, string
URL = "https://upload.wikimedia.org/wikipedia/commons/4/45/Blue_Marble_rotating.gif"
class FancyDefaultCardFlow(FlowSpec):
image_url = Parameter('image_url', default=URL)
@step
def start(self):
self.image = requests.get(self.image_url,
headers={'user-agent': 'metaflow-example'}).content
self.dataframe = pandas.DataFrame({'lowercase': list(string.ascii_lowercase),
'uppercase': list(string.ascii_uppercase)})
self.next(self.end)
@step
def end(self):
pass
if __name__ == "__main__":
FancyDefaultCardFlow()
To demonstrate how cards can be attached to runs on the fly, this example doesn’t
include the @card
decorator in the code. Save the code to fancydefaultcard.py
and
run it as follows:
python fancydefaultcard.py run --with card
Note that the example expects that you have the requests
and pandas
libraries
installed. The --with card
option attaches a @card
decorator to every step without
changes in the code. You can execute any existing flow --with card
to inspect its
results visually.
You use the local viewer to see the card or open the card manually:
python fancydefaultcard.py card view start
You will see additional sections in the card which visualize dataframe
as a table and
show the image stored in the image
artifact.
Cards come with a built-in component, VegaChart
, for creating charts and plots,
but using the above pattern you can use any other visualization library and store
the resulting image in an artifact for easy viewing.
Cards Are Stored And Versioned Automatically
A major benefit of @card
is that reports produced by it are versioned and stored in
the Metaflow datastore automatically, alongside their parent task. This way, you or
your colleagues can easily access any historical card, e.g. a model scorecard associated
with a particular version of the model.
All past cards are viewable in the Metaflow UI but you can also access any historical card on the command line by using a run ID of a past run. For instance
python fancydefaultcard.py card view 1638257165470922/start
In the case of foreach, a single step can produce multiple tasks and cards. You can view an individual card by giving a full task ID (aka pathspec) corresponding to a task:
python fancydefaultcard.py card view 1638257165470922/start/1
You can see all available cards in the latest run with the card list
command:
python fancydefaultcard.py card list
It is possible to produce multiple separate cards from a single task by adding multiple
@card
decorators in a step, which are all shown by card list
. To make it easier to
identify specific cards, you can also assign them a unique ID, as described in Multiple
Cards In a Step.
Accessing Cards via an API
Besides the Metaflow UI and the command line interface, you can access and view cards programmatically through an API. This is particularly convenient, if you want to access cards, say, in a Jupyter notebook.
Given a Task ID (a pathspec), or a Task object from the Client API, the
get_cards
function lists all cards of the task. You can try this in a notebook cell.
Replace the Task ID with an actual ID from a previous run:
from metaflow.cards import get_cards
get_cards('CountryModelFlow/1641937201798104/train_country_model/2')
This will show the card in the output cell:
The get_cards
function works well in conjunction with the Client API. For
instance, you can use the Client API to search for a task with a specific artifact and
view its card:
run = Run('CountryModelFlow/1641937201798104')
[brazil] = [task for task in run['train_country_model']
if task['country'].data == 'Brazil']
get_cards(brazil)
Sharing Cards
Since cards are self-contained HTML files, they can be easily shared and viewed by anyone without having to install additional software. To share a card, first save the desired card to a file:
python fancydefaultcard.py card get start mycard.html
Use the card get
command to save the HTML without opening it in a browser. You can
attach the resulting card file, here mycard.html
, say, in an email or a Slack message.
If you want to share reports automatically e.g. via email, you can use the get_cards
API discussed above to obtain the HTML programmatically.
Some recipients may prefer a PDF file over HTML. In this case, you can simply choose “Print” in your browser followed by “Save as PDF”.