Not logged in!

Getting Started

Quick start with feather in under 2 minutes
You can get started with feather without an API key! The only restriction is that you will not be able to publish your model. Click here to register your interest to receive an API key!
Quick Start 🛫

In this short overview, you'll see how you can use feather to implement a simple image classification model. The first step will be to install the library:

Step 1 - install the library
1pip install feather

feather uses functions to define each of the 'steps' that you see in the above feather link (i.e. the file uploader step and the output step). Let's break down the code that was used to create the interface:

Step 2 - understanding a feather script (optional)

Step 2.1 - components

The different elements that you can interact with on the above link (e.g. file uploading, viewing the images) are called components . A feather script will contain a few components which allow the user to input different kinds of data in different kinds of ways. Below is a small list of some of the components feather currently offers:

ftr.File.Upload() # Allow the end user to upload some files for inference
ftr.Text.In() # Receive a text input
ftr.Image.WithSelectOne() # The end user can select one option for each image uploaded/generated/provided
ftr.Document.WithTextIn() # Ask the end user to enter some text for each document uploaded/generated/provided
Step 2.2 - feather functions

In the simple image classification link above, there are multiple "steps" that an end user has to go through - namely uploading the files, and then viewing the output. One step is one feather function. There are 3 important concepts you should know before seeing how the code executes as a whole.

1) function inputs and outputs

All feather functions (apart from init; see next bullet point) take parameters/arguments which have been returned from the previous feather function:

1def run_object_detection(uploader): # 'uploader' has been returned from the previous feather function
2    images = uploader.get(format="images", return_only_filedata=True) # np.array of 2 images: cat, dog
3    detected_objects = my_object_detection_model(images) # [[cat, fur, ears], [dog, collar, tail]]
4    get_selected_objects = Image.WithSelectOne(images, detected_objects, title="Select an object for each image")
5    random_data = np.random.normal(size=(2,3,4))
6    return get_selected_objects, random_data
8def run_image_captionioning(get_selected_objects, random_data):
9    ### rest of code ###
2) a special init function

In feather, there is a special init function which defines the components for the first step. Like other feather functions, the init function returns the components that will be shown on the screen. However, unlike the other feather functions, init does not take any arguments.

1def init():
2    return ftr.File.Upload(types=["images"], title="Upload your images for image captioning")

You may be wondering how feather knows the order of executions of the functions. This is where comes in. lets you specify your init function, and the other functions required for your script. In addition, it asks you for the name of your model. The name field acts as a unique identifier. Any model you publish to feather will be linked with this name field you specify. Later publishes to the same name will replace the previously deployed model. description allows you to provide a short text description for what your model does. (In the future) When publishing the model, we also provide a markdown box where you can add a lengthier readme about your model., # unique model name
2                init: Callable, # your init function
3                steps: ListType[Callable], # an ordered list of the other feather functions for your model
4                description="", # your model description
5                file_bundle=None) # the file_bundle, which specifies which code and model files are required to run your model
4) file_bundle

The file_bundle parameter is now the only thing we haven't covered. file_bundle allows you to tell feather which files on your local workspace are required to run your feather script. feather makes the distinction between code_files and model_files, where each parameter takes a list of its relevant type. The filenames should be relative to the location of the feather script, and we recommend placing files of classnames in the code_files argument:

1bundle = ftr.bundle(code_files=[__file__, "", "imagenet_classes.txt"], model_files=["outputs/model.ckpt"])
__file__ refers to the current file - i.e. the feather script itself. This is also explicitly required to be passed. Some larger projects may have too many files for you to type out by hand; in this case we provide a helper that allows you to recursively get all the files in your current working directory (and sub-directories):
1code_files, model_files = ftr.helpers.get_all_files_from_curr_dir(include_model_extensions=[".ckpt"])
2bundle = ftr.bundle(code_files=code_files, 
3                    model_files=model_files)
Here, include_model_extensions by default searches for model extensions that end in [".ckpt", ".pt", ".pth"]. Any suffixes that you pass to include_model_extensions will extend the previous list. You may also blacklist omit some model extensions from being in model_files by passing a list to exclude_model_extensions. Note that the bundler does not need to specify any code files from libraries here - only your 'local' files.

Step 3 - write the code
1import feather as ftr
2import my_image_classifier from my_model # import the code which runs your model
4# 👉 STEP 1: The special init function.
5# Use the init function to define which components you want to show on the first step.
6def init():
7    # We want a File.Upload component, which only accepts image types
8    uploader = ftr.File.Upload(types=["images"])
9    return uploader
11### After each feather function, we collect the users input through the UI ###
13# 👉 STEP 2: Define the first step.
14# feather functions receive the return of the previous step.
15# Optional - use the ftr.step decorator to write a title and description for this step
16@ftr.step(title="Your image classifications", description="Click an image to view its classification!")
17def run_model(uploader):
18    images = uploader.get_image_files()
19    # images = [{name: "image1.jpg", data: np.array}, {name: "image2.jpg", data: np.array}]
20    image_data = [x["data"] for x in images]
21    classifications = my_image_classifier(image_data) # [cat, dog]
22    output = ftr.Image.View(images, classifications)
23    return output
25if __name__ == "__main__":
26    # 👉 STEP 3: Bundle your files.
27    # You need to tell feather what code and weights files your model needs to run. 
28    # __file__ refers to this current file. We also need to explicitly add this to the bundle
29    bundle = ftr.bundle(code_files=[__file__, "", "imagenet_classes.txt"], model_files=["outputs/model.ckpt"])
31    # 👉 STEP 4: Build and publish! 
32    # The "name" here will be used as an identifier to your model on the feather website.
33      # Any new models you publish with the same "name" will overwrite what is currently there
34    # Once you run this script, you'll see a local preview through which you can test and publish your model
35"Simple Image Classifier", init=init, steps=[run_model], file_bundle=bundle)
Step 4 - that's it. There is no step 4

Congratulations 🎉🎉! You've just understood the key philosophies of feather. To summarise:

  • In the context of feather, init is a special function which will define the components for the first step
  • A step (init or otherwise) returns all the information to the next step. The next step takes in this returned information as parameters/arguments. Note that all data/non-components in a return statement must be pickle serialisable.
  • All files required to run your inference, including the current feather script and model weight binaries, must be passed in the ftr.bundle method. You can use __file__ for to represent the current feather script. We provide a helper to get all files in the current working directory.
  • will take in a name for this model. Any model updates publish using this 'unqiue' name is what will be shown in the live model's URL. also must take an init, steps, and bundle argument.
    • init is the special first function which defines which components you'll have on the first step (init() above)
    • steps is a list of the rest of the feather functions required to run your model
    • bundle is the bundle object you created above

In the section below, we'll run through a multi-step example (Visual Question Generation). Alternatively, if you want to dive further into our API, visit components or using feather effectively.

Visual Question Generation ✈️

In this tutorial, we'll use the knowledge from above to deploy a multi-step Visual Question Generation model.

1import feather as ftr
2import my_object_detector, my_vqg_model from my_model # import the code which runs your model
4def init():
5    uploader = ftr.File.Upload(types=["images"], title="Select images")
6    # Just to showcase the use of multiple components in one function/step ;)
7    text_input = ftr.Text.In(title="Enter your name", default_text="feather user") 
8    return uploader, text_input
10@ftr.step(title="Visual Question Generation", description="""Choose some images to upload! In the next step,
11  an object detector will be run over the images to allow you to select some 
12  objects you want the question generated upon.""")
13def run_object_detection(uploader, text_input):
14    images = uploader.get_image_files() # equivalent to 'uploader.get(format="images")'. 
15    # images = [{name: "image1.jpg", data: np.array}, {name: "image2.jpg", data: np.array}]
17    image_data = [x["data"] for x in images]
18    detected_objects, object_features = my_object_detector(image_data, max_objects=36)
19    # detected_objects = [["dog", "frisbee", "person", "tree"], ["cat", "sink", "gloves", "fruit"]]
20    # object_features = np.array of shape [# images, max_objects, D]
22    name = text_input.get() # ["feather user"]
23    name = name[0] # "feather user"
25    select_objects = ftr.Image.WithSelectMulti(images, lists=detected_objects, 
26                  title="Hey {}! Select the objects you want a question to be generated on".format(name))
27    return select_objects, object_features
29def run_vqg(select_objects, object_features):
30    selected_objects = select_objects.get(return_all=False) # equivalent to 'select_objects.get_selected()'
31    # Returns only the objects that were selected: selected_objects = [["dog", "person"], ["cat"]]
33    images = select_objects.images # pass and access user uploaded data via components 
34    image_data = [x["data"] for x in images]
36    generated_questions = vqg_model(image_data, selected_objects, object_features) 
37    # generated_questions = ["is the dog standing next to a person?", "where is the cat sitting?"]
39    output = ftr.Image.View(images, output_text=generated_questions)
40    return output
42if __name__ == "__main__":
43    code_files, model_files = ftr.helpers.get_all_files_from_curr_dir(include_model_extensions=[".ckpt"])
44    bundle = ftr.bundle(code_files=code_files, 
45                        model_files=model_files)
47    ftr.Build(name="Visual Question Generation", init=init, steps=[run_object_detection, run_vqg], file_bundle=bundle)

That's all there is to it 😉. We recommend you to read about how to use feather effectively for best practises, and our documentation on components to get a better understanding of the different components available, the arguments they can take, and how to access data within them.