Embedding Fast.ai into your website through binder and nbinteract

Felix DMR
6 min readJan 18, 2021

Some time ago, as I was working through the fast.ai course, one of the mini-projects ended with a suggestion to serve the classifier to the general public. The suggested route, being hosted on binder.org and serving the jupyter notebook directly or through voila, was quite good, and easy to do. Voila particularly made the page quite elegant and presentable. However, the method did not play nice when embedding it in your own website. After pulling my hair out for a bit I finally got it working with nbinteract and thought I’d share the steps as a short tutorial for the rest of the world.

The issue with embedding directly

Assuming you have been following the fast.ai course. You now have a model, hosted on binder.org. Naively you might think that you can just embedded that output into an iframe tag and call it a day, however if we try that right here you can see the website is not displayed, even though going directly to the website through a link works fine.

Trying to embed a jupyter notebook hosted on binder directly results in a “refused to connect” error.

The issue here is Cross-Origin protection mechanisms within both Jupyter and voila. The page service itself does not like to be serving for a given domain, but be displayed through another domain, in this case the fact that the Jupyter notebook is hosted and serving at https://binder.org is at odds with it then being also displayed within a page hosted at https://yourblog.example. Even though mybinder.org allows some configuration changes to your hosted notebook, it seems to ignore the options relating to Cross-Origin protection.

The easiest way around this is to just host your own Jupyter server on the same network/device as your website, but I use the built-in Jekyll functionality from Github for my website, so no dice.

nbinteract to the rescue

General gist on how linking to a mybinder.org hosted Jupyter notebook behaves behind the scenes.

So Jupyter refuses to be embedded…this is where nbinteract comes in. In the end, Jupyter is really a webserver that serves a interactive webpage, the notebook, which it then connects to an interactive python kernel that it spawns and hosts. nbinteract forgoes this interactive dynamic webpage and instead produces a pre-made static website with some JavaScript that connects to the interactive python kernel spawned and hosted on another server/service. The python kernel itself is connected via websockets and thus does not care what domain/server it is hosted on and will not trigger any Cross-Origin protection.

When embedded, the Jupyter webserver sees the incomming connection using a different domain origin, and refuses connection.

In practical terms, with a given notebook on a github repo, the nbinteract conversion process creates a mybinder.org hosted Jupyter instance, and a seperate static website which has the right widgets and components being displayed. When this webpage is loaded, the JavaScript will automatically trigger the mybinder.org hosted Jupyter instance to load, and connect to the kernel it spawns, bypassing the usual Jupyter frontend.

nbinteract creates a seperate local website which connects with the python kernal, spawned by jupyter, directly. Thus avoiding the server refusing the connection.

The downside to this process is that its not a true notebook, any interactive processes need to be exposed by the JavaScript functionality created by nbinteract, and for now that is limited to widgets found inipywidget. The upside is that that covers almost all your interactive needs and now you can serve the page from wherever you want, whilst binder.org hosts the instance.

Converting the fast.ai app

For a generalised tutorial of how to use nbinteract I recommend this blog post.

If you apply the above steps directly to your app, it will appear to work, but won’t do anything when you click on “Show Widgets”. The reason for this is that nbinteract doesn’t properly recognise the fast.ai implementation of widgets. For it to work you need to replace it with ipywidgets. For the specific steps I did for my fast.ai app, keep reading.

I assume you have just finished the section on “deploying your app” in Chapter 2(or 30mins into the video lesson 3), you’ve gone through the challenges of getting the dependencies right and now have a working app on binder.

Modifying the notebook to use ipywidgets

First you have to make the key changes to the notebook itself for nbinteract to work correctly. In Paperspace or your local environment where you developed the app, navigate to the notebook in question.

  1. Replace from fastai.vision.widgets import * with import ipywidgets as widgets.
  2. Go through the notebook and ensure all the widgets such are now prefixed with widgets., for example widgets.FileUpload() and widgets.VBox().
  3. Next step add the markers to suppress notebook cell inputs and outputs as needed, with # nbi:hide_in and # nbi:hide_out respectively. I suppressed all the cells except for the output of the widgets, note that you won’t see the effect until its been processed by nbinteract
  4. Save the notebook and upload it to the github repository which you are using for the binder app.
  5. Re-run the binder page to build with the new changes and make sure it still works correctly after changing the widgets (the jupyter notebook should work as per normal, with all cells visible).

Creating the static html file to embed

For the following steps, I prefer to do it directly in the binder instance, as I can quickly change how it is displayed, but note that any changes are lost after the session.

Root folder to open and run a terminal on the mybinder.org instance
  1. Navigate to the root of jupyter, by replacing the end of the url whether it be /notebook/example.ipynb or /voila/render...ipynb with /tree.
  2. At the root, open a new terminal window
  3. In the terminal install nbinteract with pip install nbinteract
  4. Then run nbinteract notebook.ipynb -s user/repo/branch, but using your values for the inference notebook and the github repo in which it is hosted.
  5. You now have a html file with the same name at the root of the folder, download it and you can test it directly!
  6. If all is working, make any edits (I like to remove the top “show widgets” button, and only keep the bottom one) and place somewhere in your blog’s file-structure so you can embed it directly.
Running the nbinteract command in the terminal to create our html file
Download the file it just created to run locally
Running it locally and inspecting the console shows how clicking on the “Show Widgets” button will trigger a mybinder.org instance to spin up, and then connect to its kernel (the first time it does this it can take a long while). Once connected it should work functionally similar to your notebook, albeit only interacting through the widgets.
Happy with how the webpage runs locally, I now upload it into my blog directory and embed directly, and as you can see it works swimmingly well.

Conclusion and Credit

Hopefully that was clear enough and it helps you in your journey to be a Data Scientist and learning about Fast.ai. Any questions feel free to ask me and I will try my best to help out, additionally if you are interested, please visit my own blog, www.felixdmr.com and tell me what you think!

Of course this was only possible thanks to:

  • Fast.ai for their tutorials and simplifying creating classifiers and deploying them.
  • Mybinder.org for allowing us to host our notebooks on their servers.
  • nbinteract for creating this great functionality and help us better integrate it in our blogs.
  • ELC for his simple guide on using nbinteract.

--

--

Felix DMR
0 Followers

Autonomous Vehicle Simulation Engineer by day, Machine learning enthusiast by night! Tend to delve into many subjects and always trying to explore more.