Simple Image Classification Using FastAI.jl

Abid Ali Awan 10 Sep, 2021 • 8 min read
This article was published as a part of the Data Science Blogathon
The fastai library is now on Julia with similar features available in Python. In this project, we are going to train the Resnet-18 model to classify images from the ImageNet dataset in simple steps.
Image Classification Using FastAI.jl
 Source: Author

Introduction to FastAI.jl

The FastAI.jl library is similar to the fast.ai library in Python and it’s the best way to experiment with your deep learning projects in Julia. The library allows you to use state-of-the-art models that you can modify, train, and evaluate by using few lines of code. The FastAI.jl provides a complete ecosystem for deep learning which includes computer vision, Natural Language processing, tabular data, and more submodules are added every month FastAI (fluxml.ai).

In this project, we are going to use the fastai library to train an image classifier on the ImageNet dataset. The imagenette2-160 dataset is from the fastai dataset repository (https://course.fast.ai/datasets) that contains smaller size images of the things around us which range from animals to cars. The ResNet-18 model architecture is available at Deep Residual Learning for Image Recognition. We won’t be going deep into the dataset or how model architecture works, instead, we will be focusing on how fastai.jl has made deep learning easy.

Imagenet | Image Classification Using FastAI.jl

Image 1

Getting Started with FastAI.jl in Julia

For more detail visit Quickstart (fluxml.ai) as the code used in this project is driven from fastai documentation.

Local Setup

If you have Julia installed in your system, then type:

using Pkg
Pkg.add("FastAI")
Pkg.add("CairoMakie")

As shown in the image below 👇

julia
Image by author

Deepnote

For the Deepnote environment, you have to create a docker file and add:

FROM gcr.io/deepnote-200602/templates/deepnote
RUN wget https://julialang-s3.julialang.org/bin/linux/x64/1.6/julia-1.6.2-linux-x86_64.tar.gz && 
    tar -xvzf julia-1.6.2-linux-x86_64.tar.gz && 
    sudo mv julia-1.6.2 /usr/lib/ && 
    sudo ln -s /usr/lib/julia-1.6.2/bin/julia /usr/bin/julia && 
    rm julia-1.6.2-linux-x86_64.tar.gz && 
    julia  -e "using Pkg;pkg"add IJulia LinearAlgebra SparseArrays Images MAT""
ENV DEFAULT_KERNEL_NAME "julia-1.6.2"

Google Colab

For Google Colab you can follow my repo on GitHub or just create a Julia environment by adding an additional cell shown below. Installing Julia packages may take up to 15 minutes.

  1. Change Runtime to GPU for faster results.
  2. Execute the code below.
  3. Reload this page by pressing F5.
%%shell
set -e
#---------------------------------------------------#
JULIA_VERSION="1.6.2"
export JULIA_PACKAGES="CUDA IJulia CairoMakie"
JULIA_NUM_THREADS="2"
#---------------------------------------------------#
if [ -n "$COLAB_GPU" ] && [ -z `which julia` ]; then
  # Install Julia
  JULIA_VER=`cut -d '.' -f -2 <<< "$JULIA_VERSION"`
  echo "Installing Julia $JULIA_VERSION on the current Colab Runtime..."
  BASE_URL="https://julialang-s3.julialang.org/bin/linux/x64"
  URL="$BASE_URL/$JULIA_VER/julia-$JULIA_VERSION-linux-x86_64.tar.gz"
  wget -nv $URL -O /tmp/julia.tar.gz # -nv means "not verbose"
  tar -x -f /tmp/julia.tar.gz -C /usr/local --strip-components 1
  rm /tmp/julia.tar.gz
  # Install Packages
  echo "Installing Julia packages, this may take up to 15 minutes. "
  julia -e 'using Pkg; Pkg.add(["CUDA", "IJulia", "CairoMakie"]); Pkg.add(Pkg.PackageSpec(url="https://github.com/FluxML/FastAI.jl")); Pkg.precompile()' &> /dev/null
  # Install kernel and rename it to "julia"
  echo "Installing IJulia kernel..."
  julia -e 'using IJulia; IJulia.installkernel("julia", env=Dict(
      "JULIA_NUM_THREADS"=>"'"$JULIA_NUM_THREADS"'"))'
  KERNEL_DIR=`julia -e "using IJulia; print(IJulia.kerneldir())"`
  KERNEL_NAME=`ls -d "$KERNEL_DIR"/julia*`
  mv -f $KERNEL_NAME "$KERNEL_DIR"/julia  
  echo ''
  echo "Success! Please reload this page and jump to the next section."
fi

Implementation of Image Classification Using FastAI.jl

Checking the version to make sure you have a similar version as mine.

versioninfo()
Julia Version 1.6.2
Commit 1b93d53fc4 (2021-07-14 15:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Xeon(R) CPU @ 2.30GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, haswell)
Environment:
  JULIA_NUM_THREADS = 2

Import the libraries

using FastAI
import CairoMakie

Download the Dataset

We are using `loaddataset` function to import the local dataset from fastai. As you can see using a single line of code can download the dataset and provide you with access to all images location with labels.

data, blocks = loaddataset("imagenette2-160", (Image, Label))
┌ Info: Downloading
│   source = https://s3.amazonaws.com/fast-ai-imageclas/imagenette2-160.tgz
│   dest = /root/.julia/datadeps/fastai-imagenette2-160/imagenette2-160.tgz
│   progress = 1.0
│   time_taken = 2.69 s
│   time_remaining = 0.0 s
│   average_speed = 35.138 MiB/s
│   downloaded = 94.417 MiB
│   remaining = 0 bytes
│   total = 94.417 MiB
└ @ HTTP /root/.julia/packages/HTTP/5e2VH/src/download.jl:128

The data contains the location of images, and the block contains structure and images labels.

data
(mapobs(loadfile, ["/root/.julia/datadeps/fastai-imagenette2-160/imagenette2-160/train/n01440764/I…]), mapobs(parentname, ["/root/.julia/datadeps/fastai-imagenette2-160/imagenette2-160/train/n01440764/I…]))
blocks
(Image{2}(), Label{String}(["n01440764", "n02102040", "n02979186", "n03000684", "n03028079", "n03394916", "n03417042", "n03425413", "n03445777", "n03888257"]))

Exploring Image for Image Classification Using FastAI.jl

By using getobs we can check a single sample from our data and check the class. In our case the class name is n01440764 you can also rename the class as fish but for simplicity, we will be using a unique Id for the class. We can also see the image in sample 500.

image, class = sample =  getobs(data, 500)
@show class
image
sample image | Image Classification Using FastAI.jl

OR

We can check multiple photos with labels by using `getobs` and `plotsamples`.

idxs = rand(1:nobs(data), 9)
samples = [getobs(data, i) for i in idxs]
plotsamples(method, samples)
multiple samples

 

Image Classification Function

In order to load our Image data, we need to first create a function that will transform images to 128X128 and preprocesses them to randomly augmentation (sheer, stretch, rotate, flip). For labels, it will use one-hot encoding to convert string labels into integers.

method = BlockMethod(

blocks,

(

ProjectiveTransforms((128, 128)),

ImagePreprocessing(),

OneHot()

)

)
BlockMethod(Image{2} -> Label{String})

OR

You can just use the ImageClassificationSingle function to reproduce equivalent results.

method = ImageClassificationSingle(blocks)

Building model for Image Classification Using FastAI.jl

  1. First, we need to use a data loader to load image links and use the transformation method. This will convert images and Labels into integer data.
  2. `methodmodel` takes the method from the image loader and ResNet architect to build a model for training.
  3. we can simply create a loss function by adding the method into `metholossfn`. By default, the loss function is `CrossEntropy`.
  4. By using `Learner` we combine, method, data loader, optimizer which is ADAM, loss function, and Metric which is accuracy in our case.
dls = methoddataloaders(data, method)
model = methodmodel(method, Models.xresnet18())
lossfn = methodlossfn(method)
learner = Learner(model, dls, ADAM(), lossfn, ToGPU(), Metrics(accuracy))

OR

We can simply do all the above steps by using a single line of code as shown below.

learner = methodlearner(method, data, Models.xresnet18(), ToGPU(), Metrics(accuracy))

Training and Evaluation of Image Classification Model

We will be training our model on 10 Epochs with a 0.002 learning rate. As we can see our training and validation loss have decreased with every iteration but after the 5th Epoch, the validation loss has become steady.

Final Metrics:

Training = Loss:  0.07313 │ Accuracy:  98.38%

Validation = Loss: 0.59254 │ Accuracy: 83.27%

This is not bad as we haven’t cleaned our data or performed hyperparameters tuning.

fitonecycle!(learner, 10, 0.002)
Epoch 1 TrainingPhase(): 100%|██████████████████████████| Time: 0:05:50
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 1.0 │ 1.75345 │ 0.43189 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 1 ValidationPhase(): 100%|████████████████████████| Time: 0:00:19
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 1.0 │ 1.54432 │ 0.53701 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 2 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:34
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 2.0 │ 1.37105 │ 0.56158 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 2 ValidationPhase(): 100%|████████████████████████| Time: 0:00:17
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 2.0 │ 1.23731 │ 0.62673 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 3 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:32
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 3.0 │ 1.09463 │ 0.65127 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 3 ValidationPhase(): 100%|████████████████████████| Time: 0:00:17
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 3.0 │ 1.11761 │ 0.65932 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 4 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:31
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 4.0 │ 0.87518 │ 0.71549 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 4 ValidationPhase(): 100%|████████████████████████| Time: 0:00:16
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 4.0 │ 0.95252 │ 0.6995 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 5 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:29
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 5.0 │ 0.70251 │ 0.77235 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 5 ValidationPhase(): 100%|████████████████████████| Time: 0:00:16
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 5.0 │ 0.77653 │ 0.75314 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 6 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:29
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 6.0 │ 0.52883 │ 0.82565 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 6 ValidationPhase(): 100%|████████████████████████| Time: 0:00:16
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 6.0 │ 0.76827 │ 0.7698 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 7 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:28
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 7.0 │ 0.35174 │ 0.88405 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 7 ValidationPhase(): 100%|████████████████████████| Time: 0:00:16
┌─────────────────┬───────┬────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼────────┼──────────┤
│ ValidationPhase │ 7.0 │ 0.7575 │ 0.77955 │
└─────────────────┴───────┴────────┴──────────┘
Epoch 8 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:28
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 8.0 │ 0.20262 │ 0.93675 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 8 ValidationPhase(): 100%|████████████████████████| Time: 0:00:16
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 8.0 │ 0.63952 │ 0.81444 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 9 TrainingPhase(): 100%|██████████████████████████| Time: 0:03:28
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 9.0 │ 0.11359 │ 0.96763 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 9 ValidationPhase(): 100%|████████████████████████| Time: 0:00:15
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 9.0 │ 0.59102 │ 0.82806 │
└─────────────────┴───────┴─────────┴──────────┘
Epoch 10 TrainingPhase(): 100%|█████████████████████████| Time: 0:03:27
┌───────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├───────────────┼───────┼─────────┼──────────┤
│ TrainingPhase │ 10.0 │ 0.07313 │ 0.98377 │
└───────────────┴───────┴─────────┴──────────┘
Epoch 10 ValidationPhase(): 100%|███████████████████████| Time: 0:00:16
┌─────────────────┬───────┬─────────┬──────────┐
│ Phase │ Epoch │ Loss │ Accuracy │
├─────────────────┼───────┼─────────┼──────────┤
│ ValidationPhase │ 10.0 │ 0.59254 │ 0.83267 │
└─────────────────┴───────┴─────────┴──────────┘

Evaluating the model Prediction

As we can see all four photos were predicted correctly. You can also see the prediction but using `plotprediction` method.

plotpredictions(method, learner)
evaluating | Image Classification Using FastAI.jl

 

Conclusion

Julia is the future of Data Science and if you want to become the future proof you need to learn the basics. With the introduction of fastai.jl things have gotten quite easy for Julia users as they just have to write few lines of code to build state-of-the-art machine learning models. The fastai.jl is built upon flux.jl which is totally built upon Julia, unlike Python libraries which integrate other programming languages integration such as C and Java scripts. Even CUDA used in Julia is build using Julia language, not C, which gives an advantage to Julia in processing and security. Julia is better than Python in many ways as I have already discussed in my previous article Julia for Data Science :A New Age Data Science – Analytics Vidhya.

In this project, we have built an image classifier model in ImageNet data by using few lines of code and our model has performed above our expectations. We have also discovered different methods to do similar tasks in image classification and how fastai the library is powerful in exploring and predicting images. Overall, I have enjoyed using this library and there is so much to it that I didn’t discuss in this article.

I hope you experiment with this library and showcase amazing projects that are production-ready.

Source Code

The source code is available at GitHub and Deepnote.

You can follow me on LinkedIn and Polywork where I post amazing articles on data science and machine learning.

Image Sources

Image 1 -https://www.researchgate.net/publication/314646236_Global-Attributes_Assisted_Outdoor_Scene_Geometric_Labeling

The media shown in this article are not owned by Analytics Vidhya and are used at the Author’s discretion.

Abid Ali Awan 10 Sep 2021

I am a technology manager turned data scientist who loves building machine learning models and research on various AI technologies. My vision is to build an AI product that will help identify students who are struggling with mental illness.

Frequently Asked Questions

Lorem ipsum dolor sit amet, consectetur adipiscing elit,

Responses From Readers

Clear