Getting Started - Creating a Face Emotion Predictor Model
First FastAI model in under 10 mins.
- Overview of this blog
- Step 1 : Installing fast's latest version
- Step 2 : Defining an Image Search Function
- Step 3 : Downloading & Viewing a Searched image
- Cleaning the directories
- Step 4 - Organising the data
- Step 5 - Creating the DataBlock
- Step 6 - Creating the Vision Learner
- Step 7 - Predictions
- End
- Optional Cells
Overview of this blog
This blog post contains a simple fast AI model that I created by following the lesson 1 of the latest Fast AI course. I have used a different dataset here and have almost replicated the steps used by Jeremy to train and predict an image classifier model. Note:
- This Blog post is written with an intention of learning how to use Jupyter Notebooks with fastpages to create blog posts.
- This is not a very verbose tutorial, as my aim was to just get a hands on into the FastAi's code, and create a simple blog post out of it.
- Future blogs that I'll write will focus more on the actual definitions of concepts/techniques and code by code walkthrough of the notebook that I'll create as I'll follow the course along.
- I have added "Commands" learned section at the bottom of the blog, to provide under useful (and can be basic) commands that I have learned as part of the notebook.
pip install -Uqq fastbook
from fastcore.all import *
import time
def search_images(term, max_images=200):
url = 'https://duckduckgo.com/'
res = urlread(url,data={'q':term})
searchObj = re.search(r'vqd=([\d-]+)\&', res)
requestUrl = url + 'i.js'
params = dict(l='us-en', o='json', q=term, vqd=searchObj.group(1), f=',,,', p='1', v7exp='a')
urls,data = set(),{'next':1}
while len(urls)<max_images and 'next' in data:
data = urljson(requestUrl,data=params)
urls.update(L(data['results']).itemgot('image'))
requestUrl = url + data['next']
time.sleep(0.2)
return L(urls)[:max_images]
from fastdownload import download_url
dest = 'person_sad.jpg'
download_url(urls[0], dest, show_progress=False)
from fastai.vision.all import *
im = Image.open(dest)
im.to_thumb(256,256)
download_url(search_images('happy person photos', max_images=1)[0], 'person_happy.jpg', show_progress=False)
Image.open('person_happy.jpg').to_thumb(256,256)
import shutil
downloaded_path = Path('happy_sad_angry_downloaded')
resized_path = Path("happy_sad_angry_resized")
shutil.rmtree(downloaded_path)
shutil.rmtree(resized_path)
Step 4 - Organising the data
- Download the different categories of images in happy_sad_angry_downloaded directory
- Resizing all the images downloaded and saving the resized images in happy_sad_angry_resized directory
- Some photos might not download correctly which could cause our model training to fail, hence remove them:
searches = 'happy person','sad person','angry person'
downloaded_path = Path('happy_sad_angry_downloaded')
resized_path = Path("happy_sad_angry_resized")
for o in searches:
dest_downloaded = (downloaded_path/o)
dest_downloaded.mkdir(exist_ok=True, parents=True)
download_images(dest_downloaded, urls=search_images(f'{o} photo'))
resize_images(downloaded_path/o, max_size=400, dest=resized_path/o)
failed = verify_images(get_image_files(resized_path))
failed.map(Path.unlink)
len(failed)
Step 5 - Creating the DataBlock
To train a model, we'll need DataLoaders, which is an object that contains a :
- Training set (the images used to create a model) and a ;
- Validation set (the images used to check the accuracy of a model -- not used during training). In fastai we can create that easily using a DataBlock, and view sample images from it:
DataBlock API
- The inputs are going to be images “ImageBlock” and the outputs are going to be categories “CategoryBlock”.
-
get_image_files
is used to get the items we require - We define a splitter to split the dataset into Training & Validation Set. In this case, we are using a
RandomSplitter
with 20% data for validation -
get_y
takes the label for the images. Here,parent_label
is the name of the parent (or folder) for each image, i.e., happy person, sad person, angry person - Before training, resize each image to 192x192 pixels by "squishing" it (as opposed to cropping it).
dls = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(valid_pct=0.2, seed=42),
get_y=parent_label,
item_tfms=[Resize(192, method='squish')]
).dataloaders(resized_path)
dls.show_batch(max_n=6)
Step 6 - Creating the Vision Learner
Here is where the actual magic happens. Yes, we're not aware about the fun calculations underneath at the moment, hence, let's call it as magic ;).
- We're now training the model using the dataloader that we created in the previous step.
- We define error_rate as our metrics, which is nothing but the mean squared error
- We provide
resnet18
as the architecture(pre-trained model) to train our model. This is the basis of transfer learning, which will be covered in the later blogs.
learn = vision_learner(dls, resnet18, metrics=error_rate)
learn.fine_tune(3)
is_happy,x,probs = learn.predict(PILImage.create('person_happy.jpg'))
print(f"This is a: {is_happy}.")
print(f"Probability the person is happy: {probs[1]:.4f}")
print(learn.predict(PILImage.create('person_happy.jpg')))
print(learn.predict(PILImage.create('person_sad.jpg')))
End
As you can see, we have successfully trained a model and predicted results in well under 10 mins. This was a very brief introduction blog post to the image classification model in the fastAI, with almost no tweaks in the parameters, or pre-trained models, or data augmentations. The below section is completely optional, and just provide some additional commands that are tend to be useful in general, or which I have learned as completely new while doing this project.
- ##### This cell can be run if you want to delete the whole folder along with the contents. This will delete any/all directories and files that are present inside path object.
import shutil
path = Path('resized_fruits')
shutil.rmtree(path)
- ##### Deleting only Files in a directory
flag_search = 'happy person'
path = Path('happy_sad_angry')
dest_flag_search = (path/flag_search) # Path to the "happy person" folder
files = os.listdir(dest_flag_search)
for fi in files:
print(fi)
os.unlink(dest_flag_search/fi)
- ##### Deleting a Folder (Note: This will only work when the folders are empty)
searches = 'happy person','sad person','angry person'
path = Path('happy_sad_angry')
for o in searches:
os.rmdir(path/o)
Challenges/Errors Faced
- Same File error - See post : https://forums.fast.ai/t/same-file-path-error-while-resizing-images-lesson-1/97601 Description : While running the resize_images() method, the resized images were created with the same file name and path as of downloaded images, and hence the error was producing. Although, I'm not sure, why this error didn't appear in the original notebook for lesson 1.
Useful Commands learned
OS Specific
- To create a path to a folder or file :-
path = Path(happy_sad_angry/abc.jpg)
- To delete a directory :-
os.rmdir(<path>)
- To delete files in a folder :-
os.unlink(<filepath>)
- To list directories and files in a folder
os.listdir(<path>)
- To install latest versions of a library
!pip install -Uqq fastbook
- To get the os environmentYou can also use
os.environ()
os.environ().get(<key>)
FastAI Specific
- To download using URL
from fastdownload import download_url download_url(<url>, <dest_file>, show_progress=False)
- FastAI's vision imports
from fastai.vision.all import *
- To download and resize images to equal resolution
download_images(<download_path>, urls= <list of url>) resize_images(<downloaded_path>, max_size=400, dest=<resized_path>)
- To open an image from the path
PILImage.create('person_sad.jpg')
- To predict provided an item : Returns label(or category), index to look from the probability tensor, probabilities for all category (as a tensor)Output : ('sad person', TensorBase(2), TensorBase([3.2509e-03, 1.1917e-04, 9.9663e-01]))
learn.predict(<item>)
Pythonic Image
- To open an image in lazy manner (i.e., it identifies the file, but the file remains open and the actual image data is not read from the file until you try to process the data) and set thumbnail to 256*256
im = Image.open(<dest_file>) im.to_thumb(256,256)
Pythonic Strings
- Formatted StringsOutput : happy person photo
o = 'happy person f'{o} photo'