Home of this article. The global objective of these articles is to build a coin classifier, capable of scanning your pocket change and find rare / valuable coins. This is a second article in a series, so let me remind you what happened earlier.  During previous step we got a rather large dataset composed of pairs of images, loaded from an online coins site meshok.ru. Those images were uploaded to the Internet by people we do not know, and though they are supposed to contain coin's head in one image and tail in the other, we can not rule out a situation when we have two heads and no tail and vice versa. Also at the moment we have no idea which image contains head and which contains tail: this might be important when we feed data to our final classifier. So let's write a program to distinguish heads from tails. It is a rather simple task, involving a convolutional neural network that is using transfer learning. Same way as before, we are going to use Google Colab environment, taking the advantage of a free video card they grant us an access to. We will store data on a Google Drive, so first thing we need is to allow Colab to access the Drive: Next step, we are going to install the Efficient Net. This is the pretrained network (remember I spoke about transfer learning?) that we use as a starting point, rather than training a network from scratch.  Next, i usually have a large "include" section, please note that some files may be included that are not really used: feel free to delete them: Let's see which version of Tensorflow is used. This step is important, as Google is known for suddenly changing (increasing) versions: The output in my case was: Then we do some additional initializations. Setting directories where our project is, and some subfolders for weight stored during training: We only train once, why would we do it every time, right? So we are going to use the boolean flag, if false, it means that 	training was already done, weights are stored in files, and instead of re-training, we can simply load those weights: We are going to scale down images to 256x256, use batch size 8 during training, and so on: here are constants we will need. Names are self-explainatory. We are also going to break our data to training images (used to tune network's weights), validation images used to calculate performance on data the net never saw) and the rest (testing data, used to test the result). We have two classes for our classifier to distinguish between: Let's load data by reading the "head" and "tail" folders' content:  Function to load images: For an array of image info (file names and so on), get max indexes of training, validation and testing subsets: It is always a good idea to make sure everything works as intended, so let's test image loading: To make our dataset more diverse (augmentation), we might want to add noise to images: We will need the ImageDataGenerator to produce augmented images: The following function is used to get an image by index from data we loaded earlier, using image data generator we just created: Again, we need to make sure everything works, so let's see what this function returns: If we do training and for some reason want to start it over, we need to delete network we saved by that time:  As we train our network, it accumulates "history". It is a good idea to be able to show it as a chart, this way we can often see if training can be improved: Now a function that creates a model. It loads the EfficientNet, removes its last layers (the classifier) and attaches our own classifier, one we are going to train: The following class is used to produce batches of images (and labels) that are used during training. Sequence class that is used as a parent is a new standard of Keras (if you don't want to use tfdata), it is highly paralelizeable and convenient: We will need two objects of this class, one for training and one for validation: As usual, we need a function to show image obtained this way: And (again, as usual) we want to test the result: We want to be able to stop training any time and later start from where we left, so we need to save weights at the end of each epoch. To do it, we create a list of callbacks and use it during training. Also, we need to be able to load the model (to continue training or to do testing): The following function does actual training: As you can see, it does some initializations, and then calls Keras's "fit" function.   Another data generator. This one reads images that we use AFTER network was trained. We don't care about labels here, as we deal with test set (or pretend it is test data). As usual, load image using this generator: And display it: Using the generator above, we can load all test images and run prediction on them: By the way, we can get accuracies for all our predictions: Finally, here is the function that STARTS the training. It has somewhat confusing name "test": We can now call this function and therefore start training: After training is complete, we can run predictions on all test data: Ok, our model is trained and tested on a test data set. Now we can actually USE it: we can load a HUGE set of images and classify them (see comments in code). Note that this code is written to work with image file names convention from previous step: As the result, we have file names with "_head" or "_tail" suffix, and coins that have no pair are removed.