CNN   - Overview / Implementation on TensorFlow


# CNN　（Convolutional Neural Network）
1. [Fundamental information](#fundamental-information)
1. [Data Preprocessing](#data-preprocessing)
1. [Building the CNN](#building-the-cnn)
1. [Training the CNN model](#training-the-cnn-model)
1. [Making a single prediction](#making-a-single-prediction)
1. [MATH](#math)
1. [Reference](#reference)




<a id="#fundamental-information"></a>
# Fundamental information
1. ***Convolution***
    - Cover the Feature Detector(Filter) onto Input Image and multiply each value by each value, e.g. top-left x top-left
    - preserve the special relationships between pixels!!
    - *Input Image x Feature Detector = Feature Map*
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/1e4387fa-6ab8-9e29-ad49-b27f0f4413d5.png)
   https://medium.com/@bdhuma/6-basic-things-to-know-about-convolution-daef5e1bc411


1. ***ReLU(Rectified Linear Unit) Layer***
     - = Activation Function
     - to remove any linearity or increase nonlinearity in our images
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/31fbedc0-a052-91cd-3041-99fabfa750e2.png)
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/e87229c7-5dd2-8421-1950-41810a00ce75.png)




1. ***Pooling***
   - provide "spatial invariance" in our images
   - significantly reduces the size of our images
   - avoid overfitting
   - preserve the main features;)
   - *Max Pooling*:
     <img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/df841745-fa15-f076-c0cd-62607bbe18c2.png">




1. ***Flattening***
     - Flatten all of the pooled images into one long *vectors/column*
        <img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/abf423d7-f41b-71ed-3511-fbae22fd0bcf.png">




1. ***Full Connection***
    - i.e.
       <img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/0756c9eb-94d1-a738-d2e4-40e4b256c215.png">

- conclusion:
    <img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089224/65b8cc85-43b8-bf96-c3ca-ec8c5170d264.png">

---

<em><strong> ⬇️Build CNN⬇️ </strong>
- binary classification problem (image classification)
- CNN(Convolutional neural network)
- 1st〜full_connection layer: relu
- output layer: sigmoid</em>


<a id="#Data-preprocessing"></a>
# Data Preprocessing


1. ***Import Libraries***
    - Initialise CNN-network
    - Create cnn variable which represents the CNN-network (sequential)
     ```
     import tensorflow as tf 
     from keras.preprocessing.image import ImageDataGenerator 
         # library:keras / module:preprocessing / submodule:image / class:ImageDataGenerator
         # used for data preprocessing (next step)
     ```
     ```
     tf.__version__
       # print the version of TensorFlow
     ```

1. ***Preprocess Training Set***
     - Mount google-drive if using google-colab
     ```
     from google.colab import drive
     drive.mount('/content/drive')
     ```

    - generaete the traing_data
     ```
     #using keras API
     train_datagen = ImageDataGenerator(rescale = 1./255, 
                                             #feature scaling => applying to each and every single pixels      
                                                                 # by dividing their value by 255. (0 - 1) i.e. normalization
                                        shear_range = 0.2,
                                        zoom_range = 0.2,
                                        horizontal_flip = True)
     ```

     - import the training img_set(target_size, batch...)
     ```
     training_set = train_datagen.flow_from_directory('dataset/training_set', 
                   # take train_datagen object (instance of ImageDataGenerator class)and call the      method(function) of ImageDataGenerator class
                                                      target_size = (64, 64), 
                                                      batch_size = 32, #how many images we want to have in each batch (32 is default)
                                                      class_mode = 'binary') #classification(0 or 1)
     ```





1. ***Preprocess Test Set***
    - generate the training_set
     ```    
     test_datagen = ImageDataGenerator(rescale = 1./255)
     ```

    - import the test img_set(target_size, batch...)
     ```    
     test_set = test_datagen.flow_from_directory('dataset/test_set',
                                                 target_size = (64, 64),
                                                 batch_size = 32,
                                                 class_mode = 'binary')
     ```





<a id="#building-the-cnn"></a>
# Building the CNN

<em>1st LAYER(1. Convolution & 2. Pooling)</em>

1. ***Convolution***
    - Initialize CNN-network
    - Create cnn variable which represents the CNN-network (sequential)
     ```
     cnn = tf.keras.models.Sequential() 
          # tf:tensorflow
              # keras: library
                    # models:module
                           # Sequential:class
     ```

    - Apply Convolution: Input Image => [Feature Detector/Filter] => Feature Map => ReLU Activation Function   
    ```
    cnn.add(tf.keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu', input_shape=[64, 64, 3]))
    #cnn:object
        #add:method
                    #layers:module
                            #Conv2D():class
                                  # filters: = feature Detector:the # of filters
                                  # kernel_size: dimention of the filter
                                  # activation: rectifier
                                  # input_shape: 3D(RGB)//initially reshaped this time
    ```
1. ***Pooling***
    - Apply Max Pooling: Feature Map => [Max Pooling] =>Pooled Feature Map
     ```
     cnn.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))
                             # MaxPool2D: make feature map => pooled feature map
                                       # pool_size:size(width/high)e.g.,2x2 in this case
                                                    # strides: Which # of pixel is the fram shifted to the right
     ```

     ---
     <em>2nd LAYER (repeat previous 2 layers)</em>

1. ***Add additional Layers(Convolution/Pooling)***
    - Repeat the exact same codes (Convolution/Pooling)
     ```
     cnn.add(tf.keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu'))
                                                                       # !! remove input_shape !!
     cnn.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))
     ```

     ---

1. ***Flattening***
    - Flatten all of the pooled images into one long vector/column
     ```
     cnn.add(tf.keras.layers.Flatten())
                             # Flatten():class/ no parameter needed

     ```

     ---
     <em>NEURAL NETWORK BEGINS here</em>

1. ***Full Connection (dense)***
     ```
     cnn.add(tf.keras.layers.Dense(units=128, activation='relu'))
                             # Dense():class / used only when FULLY connecting
                                  # units:the # of hidden nurons
                                             # activation: !!still not the final layer!!
     ```

1. ***Output Layer (dense)***
    
     ```
     cnn.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))
                                  # units:the # of output nuron (only 1 output - classification)
                                           # activation: sigmoid:binary /softmax:multi-class
     ```



<a id="#training-the-cnn-model"></a>
# Training the CNN model

1. ***Compile the CNN***
    - Connect CNN to an Optimizer, Lost Function, and some Metrics
     ```
     cnn.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
    # compile: method
              # optimizer, loss, metrics : Parameter names
                # adam: stochastic gradient descent (update the weights in order to reduce the loss error between the predictions and targets)
                                    # binary_crossentropy: for Binary Classification task
                                                                  # metrics: measure the performance of the classification model
     ```
1. ***Train CNN(Training set) & Evaluate(Test set)***
    - this time, do training and evaluation at the same time
     ```
     cnn.fit(x = training_set, validation_data = test_set, epochs = 25)
        # fit: method for training CNN
           # x, validation_data, epochs : parameter names
             # training_set: @Data Preprocessing - Training set
                              # test_set: @Data Preprocessing - Test set
     ```
    





<a id="#making-a-single-prediction"></a>
# Making a single prediction

- Import
```
import numpy as np
               # np: shortcut 

from keras.preprocessing import image
            # preprocessing: module
                                # image: module
                                      # NOTE: "from keras.preprocessing.image import ImageDataGenerator" <= specifically downloading ImageDataGenerator, so to use "image" module, we need to download it individually.
```

- Test image: 
   - Load/ Define its size as same as training/test set
   - Convert from pil to Numpy Array
   - Update to BATCH dimension
```
test_image = image.load_img('dataset/single_prediction/cat_or_dog_1.jpg', target_size = (64, 64))
#test_image: variable
            # image: sub-module
                  # load_img('', target_size=): function / take 2 arguments
                                                                         # target_size: SAME SAZE as training/test set

test_image = image.img_to_array(test_image)
            # convert img format - "pil" => Numpy Array

test_image = np.expand_dims(test_image, axis = 0)
# Update the test_image by adding extra DIMENSION corresponding to the BATCH
            # np: (test_image = numpy array)
                # expand_dims():add a fake dimension corresponding to the batch 
                                        # axis: BATCH = 1st Dimension / Dimension of the batch of test_image is the first dimension /(where we want to add the extra dimension)


result = cnn.predict(test_image)
            # predict: method / return 0 or 1
```


- Clarify output label
   - class_indices
```
training_set.class_indices
# figure out which(dog/cat) is 0 or 1 /(call the class_indices attribute from the training_set object)
            # class_indices: attributeee

if result[0][0] == 1:
  # result has a BATCH Dimension, therefore [][]
        # [only one batch][1st and only elemet(prediction/output)]
  prediction = 'dog'
else:
  prediction = 'cat'    
```


<a id="#math"></a>
# MATH



<a id="#reference"></a>
# Reference
- Gradient-Based Learning Applied to Document Recognition (written by Yann LeCun et al)
- Deep Learning A-Z™: Hands-On Artificial Neural Networks (udemy)
- Data Science: Deep Learning and Neural Networks in Python (udemy)
- wikipedia
- https://keras.io/
- https://www.tensorflow.org/
- https://pytorch.org/
- https://medium.com/analytics-vidhya/beginners-guide-to-convolutional-neural-network-62c48043e262
- https://www.researchgate.net/figure/Overview-and-details-of-a-convolutional-neural-network-CNN-architecture-for-image_fig2_341576780