# **Machine Learning Class11 (Part 1)**
# **Convolutional Neural network (CNN)**
> CNN常常被用在影像處理上,它的theory base就是三個property(屬性),和兩個架構
convolution 架構:針對property 1和property 2
max pooling架構:針對property 3

## **Why CNN for Image?**
### **CNN V.s. DNN**
我們當然可以用一般的neural network來做影像處理,不一定要用CNN,比如說,你想要做圖像的分類,那你就去train一個neural network,它的input是一張圖片,你就用里面的pixel來表示這張圖片,也就是一個很長很長的vector,而output則是由圖像類別組成的vector,假設你有1000個類別,那output就有1000個dimension
但是,我們現在會遇到的問題是這樣子:實際上,在train neural network的時候,我們會有一種期待說,在這個network structure里面的每一個neuron,都應該代表了一個最基本的classifier;事實上,在文獻上,根據訓練的結果,也有很多人得到這樣的結論,舉例來說,下圖中:
第一個layer的neuron,它就是最簡單的classifier,它做的事情就是detect有沒有綠色出現、有沒有黃色出現、有沒有斜的條紋出現等等
那第二個layer,它做的事情是detect更覆雜的東西,根據第一個layer的output,它如果看到直線橫線,就是窗框的一部分;如果看到棕色的直條紋就是木紋;看到斜條紋加灰色的,這個有可能是很多東西,比如說,輪胎的一部分等等
再根據第二個hidden layer的output,第三個hidden layer會做更覆雜的事情,比如它可以知道說,當某一個neuron看到蜂巢,它就會被activate;當某一個neuron看到車子,它就會被activate;當某一個neuron看到人的上半身,它就會被activate等等

那現在的問題是這樣子:**當我們直接用一般的fully connected的feedforward network來做圖像處理的時候,往往會需要太多的參數**
舉例來說,假設這是一張100*100的彩色圖片,它的分辨率才100*100,那這已經是很小張的image了,然後你需要把它拉成一個vector,總共有100*100*3個pixel(如果是彩色的圖的話,每個pixel其實需要3個value,即RGB值來描述它的),把這些加起來input vectot就已經有三萬維了;如果input vector是三萬維,又假設hidden layer有1000個neuron,那僅僅是第一層hidden layer的參數就已經有30000*1000個了,這樣就太多了
所以,**CNN做的事情其實是,來簡化這個neural network的架構,我們根據自己的知識和對圖像處理的理解,一開始就把某些實際上用不到的參數給過濾掉**,我們一開始就想一些辦法,不要用fully connected network,而是用比較少的參數,來做圖像處理這件事情,所以CNN其實是比一般的DNN還要更簡單的
雖然CNN看起來,它的運作比較覆雜,但事實上,它的模型比DNN還要更簡單,我們就是用prior knowledge,去把原來fully connected的layer里面的一些參數拿掉,就變成CNN
### **Three Property for CNN theory base**
為什麽我們有可能把一些參數拿掉?為什麽我們有可能只用比較少的參數就可以來做圖像處理這件事情?下面列出三個對影像處理的觀察:(這也是CNN架構提出的基礎所在!!!)
#### **Some patterns are much smaller than the whole image**
在影像處理里面,如果在network的第一層hidden layer里,那些neuron要做的事情是偵測有沒有一種東西、一種pattern(圖案樣式)出現,那大部分的pattern其實是比整張image要小的,所以對一個neuron來說,想要偵測有沒有某一個pattern出現,它其實並不需要看整張image,只需要看這張image的一小部分,就可以決定這件事情了

舉例來說,假設現在我們有一張鳥的圖片,那第一層hidden layer的某一個neuron的工作是,檢測有沒有鳥嘴的存在(你可能還有一些neuron偵測有沒有鳥嘴的存在、有一些neuron偵測有沒有爪子的存在、有一些neuron偵測有沒有翅膀的存在、有沒有尾巴的存在,之後合起來,就可以偵測,圖片中有沒有一只鳥),那它其實並不需要看整張圖,因為,其實我們只要給neuron看這個小的紅色杠杠里面的區域,它其實就可以知道說,這是不是一個鳥嘴,對人來說也是一樣,只要看這個小的區域你就會知道說這是鳥嘴,所以,**每一個neuron其實只要連接到一個小塊的區域就好,它不需要連接到整張完整的圖,因此也對應著更少的參數**
#### **The same patterns appear in different regions**
同樣的pattern,可能會出現在image的不同部分,但是它們有同樣的形狀、代表的是同樣的含義,因此它們也可以用同樣的neuron、同樣的參數,被同一個detector檢測出來

舉例來說,上圖中分別有一個處於左上角的鳥嘴和一個處於中央的鳥嘴,但你並不需要訓練兩個不同的detector去專門偵測左上角有沒有鳥嘴和中央有沒有鳥嘴這兩件事情,這樣做太冗余了,我們要cost down(降低成本),我們並不需要有兩個neuron、兩組不同的參數來做duplicate(重覆一樣)的事情,所以**我們可以要求這些功能幾乎一致的neuron共用一組參數,它們share同一組參數就可以幫助減少總參數的量**
#### **Subsampling the pixels will not change the object**
我們可以對一張image做subsampling(二次抽樣),假如你把它奇數行、偶數列的pixel拿掉,image就可以變成原來的十分之一大小,而且並不會影響人對這張image的理解,對你來說,下面兩張大小不一的image看起來不會有什麽太大的區別,你都可以識別里面有什麽物件,因此subsampling對圖像辨識來說,可能是沒有太大的影響的

所以,**我們可以利用subsampling這個概念把image變小,從而減少需要用到的參數量**
## The whole CNN structure
整個CNN的架構是這樣的:
首先,input一張image以後,它會先通過Convolution的layer,接下來做Max Pooling這件事,然後再去做Convolution,再做Maxi Pooling...,這個process可以反覆進行多次(重覆次數需要事先決定),這就是network的架構,就好像network有幾層一樣,你要做幾次convolution,做幾次Max Pooling,在定這個network的架構時就要事先決定好
當你做完先前決定的convolution和max pooling的次數後,你要做的事情是Flatten,做完flatten以後,你就把Flatten output丟到一般的Fully connected network里面去,最終得到影像辨識的結果

我們基於之前提到的三個對影像處理的觀察,設計了CNN這樣的架構,第一個是要偵測一個pattern,你不需要看整張image,只要看image的一個小部分;第二個是同樣的pattern會出現在一張圖片的不同區域;第三個是我們可以對整張image做subsampling
那**前面這兩個property,是用convolution的layer來處理的;最後這個property,是用max pooling來處理的**

### **Convolution**
假設現在我們network的input是一張6x6的image,圖像是黑白的,因此每個pixel只需要用一個value來表示,而在convolution layer里面,有一堆Filter,這邊的每一個Filter,其實就等同於是Fully connected layer里的一個neuron
#### **Property 1**

每一個Filter其實就是一個matrix,這個matrix里面每一個element的值,就跟那些neuron的weight和bias一樣,是network的parameter,它們具體的值都是通過Training data學出來的,而不是人去設計的
所以,每個Filter里面的值是什麽,要做什麽事情,都是自動學習出來的,上圖中每一個filter是3x3的size,意味著它就是在偵測一個3x3的pattern,**當它偵測的時候,並不會去看整張image,它只看一個3x3範圍內的pixel,就可以判斷某一個pattern有沒有出現,這就考慮了property 1**
#### **Property 2**
這個filter是從image的左上角開始,做一個slide window,每次向右挪動一定的距離,這個距離就叫做stride,由你自己設定,每次filter停下的時候就跟image中對應的3x3的matrix做一個內積(相同位置的值相乘並累計求和),這里假設stride=1,那麽我們的filter每次移動一格,當它碰到image最右邊的時候,就從下一行的最左邊開始重覆進行上述操作,經過一整個convolution的process,最終得到下圖所示的紅色的4x4 matrix

觀察上圖中的Filter1,它斜對角的地方是1,1,1,所以它的工作就是detect有沒有連續的從左上角到右下角的1,1,1出現在這個image里面,檢測到的結果已在上圖中用藍線標識出來,此時filter得到的卷積結果的左上和左下得到了最大的值,這就代表說,該filter所要偵測的pattern出現在image的左上角和左下角
**同一個pattern出現在image左上角的位置和左下角的位置,並不需要用到不同的filter,我們用filter1就可以偵測出來,這就考慮了property 2**
#### **Feature Map**
在一個convolution的layer里面,它會有一打filter,不一樣的filter會有不一樣的參數,但是這些filter做卷積的過程都是一模一樣的,你把filter2跟image做完convolution以後,你就會得到另外一個藍色的4x4 matrix,那這個藍色的4x4 matrix跟之前紅色的4x4matrix合起來,就叫做Feature Map(特征映射),有多少個filter,對應就有多少個映射後的image

CNN對**不同scale的相同pattern的處理上存在一定的困難**,由於現在每一個filter size都是一樣的,這意味著,如果你今天有同一個pattern,它有不同的size,有大的鳥嘴,也有小的鳥嘴,CNN並不能夠自動處理這個問題;DeepMind曾經发過一篇paper,上面提到了當你input一張image的時候,它在CNN前面,再接另外一個network,這個network做的事情是,它會output一些scalar,告訴你說,它要把這個image的里面的哪些位置做旋轉、縮放,然後,再丟到CNN里面,這樣你其實會得到比較好的performance
#### **Colorful image**
剛才舉的例子是黑白的image,所以你input的是一個matrix,如果今天是彩色的image會怎麽樣呢?我們知道彩色的image就是由RGB組成的,所以一個彩色的image,它就是好幾個matrix疊在一起,是一個立方體,如果我今天要處理彩色的image,要怎麽做呢?

這個時候你的filter就不再是一個matrix了,它也會是一個立方體,如果你今天是RGB這三個顏色來表示一個pixel的話,那你的input就是3*6*6,你的filter就是3*3*3,你的filter的高就是3,你在做convolution的時候,就是把這個filter的9個值跟這個image里面的9個值做內積,可以想象成filter的每一層都分別跟image的三層做內積,得到的也是一個三層的output,每一個filter同時就考慮了不同顏色所代表的channel
#### **Convolution V.s. Fully connected**
##### **filter是特殊的”neuron“**
接下來要講的是,convolution跟fully connected有什麽關系,你可能覺得說,它是一個很特別的operation,感覺跟neural network沒半毛錢關系,其實,它就是一個neural network
convolution這件事情,其實就是fully connected的layer把一些weight拿掉而已,下圖中綠色方框標識出的feature map的output,其實就是hidden layer的neuron的output

接下來我們來解釋這件事情:
如下圖所示,我們在做convolution的時候,把filter放在image的左上角,然後再去做inner product,得到一個值3;這件事情等同於,我們現在把這個image的6*6的matrix拉直變成右邊這個用於input的vector,然後,你有一個紅色的neuron,這些input經過這個neuron之後,得到的output是3

##### **每個“neuron”只檢測image的部分區域**
那這個neuron的output怎麽來的呢?這個neuron實際上就是由filter轉化而來的,我們把filter放在image的左上角,此時filter考慮的就是和它重合的9個pixel,假設你把這一個6*6的image的36個pixel拉成直的vector作為input,那這9個pixel分別就對應著右側編號1,2,3的pixel,編號7,8,9的pixel跟編號13,14,15的pixel
如果我們說這個filter和image matrix做inner product以後得到的output 3,就是input vector經過某個neuron得到的output 3的話,這就代表說存在這樣一個neuron,這個neuron帶weight的連線,就只連接到編號為1,2,3,7,8,9,13,14,15的這9個pixel而已,而這個neuron和這9個pixel連線上所標注的的weight就是filter matrix里面的這9個數值
作為對比,Fully connected的neuron是必須連接到所有36個input上的,但是,我們現在只用連接9個input,因為我們知道要detect一個pattern,不需要看整張image,看9個input pixel就夠了,所以當我們這麽做的時候,就用了比較少的參數
##### **“neuron”之間共享參數**
當我們把filter做stride = 1的移動的時候,會发生什麽事呢?此時我們通過filter和image matrix的內積得到另外一個output值-1,我們假設這個-1是另外一個neuron的output,那這個neuron會連接到哪些input呢?下圖中這個框起來的地方正好就對應到pixel 2,3,4,pixel 8,9,10跟pixel 14,15,16

你會发現output為3和-1的這兩個neuron,它們分別去檢測在image的兩個不同位置上是否存在某個pattern,因此在Fully connected layer里它們做的是兩件不同的事情,每一個neuron應該有自己獨立的weight
但是,當我們做這個convolution的時候,首先我們把每一個neuron前面連接的weight減少了,然後我們強迫某些neuron(比如上圖中output為3和-1的兩個neuron),它們一定要共享一組weight,雖然這兩個neuron連接到的pixel對象各不相同,但它們用的weight都必須是一樣的,等於filter里面的元素值,這件事情就叫做weight share,當我們做這件事情的時候,用的參數,又會比原來更少
##### **總結**
因此我們可以這樣想,有這樣一些特殊的neuron,它們只連接著9條帶weight的線(9=3x3對應著filter的元素個數,這些weight也就是filter內部的元素值,上圖中圓圈的顏色與連線的顏色一一對應)
當filter在image matrix上移動做convolution的時候,每次移動做的事情實際上是去檢測這個地方有沒有某一種pattern,對於Fully connected layer來說,它是對整張image做detection的,因此每次去檢測image上不同地方有沒有pattern其實是不同的事情,所以這些neuron都必須連接到整張image的所有pixel上,並且不同neuron的連線上的weight都是相互獨立的
==**那對於convolution layer來說,首先它是對image的一部分做detection的,因此它的neuron只需要連接到image的部分pixel上,對應連線所需要的weight參數就會減少;其次由於是用同一個filter去檢測不同位置的pattern,所以這對convolution layer來說,其實是同一件事情,因此不同的neuron,雖然連接到的pixel對象各不相同,但是在“做同一件事情”的前提下,也就是用同一個filter的前提下,這些neuron所使用的weight參數都是相同的,通過這樣一張weight share的方式,再次減少network所需要用到的weight參數**==
**CNN的本質,就是減少參數的過程**
##### **補充**
看到這里你可能會問,這樣的network該怎麽搭建,又該怎麽去train呢?
首先,第一件事情就是這都是用toolkit做的,所以你大概不會自己去寫;如果你要自己寫的話,它其實就是跟原來的Backpropagation用一模一樣的做法,只是有一些weight就永遠是0,你就不去train它,它就永遠是0
然後,怎麽讓某些neuron的weight值永遠都是一樣呢?你就用一般的Backpropagation的方法,對每個weight都去算出gradient,再把本來要tight在一起、要share weight的那些weight的gradient平均,然後,讓他們update同樣值就ok了
### **Max Pooling**
#### **Operation of max pooling**
相較於convolution,max pooling是比較簡單的,它就是做subsampling,根據filter 1,我們得到一個4*4的matrix,根據filter 2,你得到另外一個4*4的matrix,接下來,我們要做什麽事呢?
我們把output四個分為一組,每一組里面通過選取平均值或最大值的方式,把原來4個value合成一個 value,這件事情相當於在image每相鄰的四塊區域內都挑出一塊來檢測,這種subsampling的方式就可以讓你的image縮小!

講到這里你可能會有一個問題,如果取Maximum放到network里面,不就沒法微分了嗎?max這個東西,感覺是沒有辦法對它微分的啊,其實是可以的,後面的章節會講到Maxout network(參考Class 10),會告訴你怎麽用微分的方式來處理它
### **Convolution + Max Pooling**
所以,結論是這樣的:
做完一次convolution加一次max pooling,我們就把原來6x6的image,變成了一個2x2的image;至於這個2x2的image,它每一個pixel的深度,也就是每一個pixel用幾個value來表示,就取決於你有幾個filter,如果你有50個filter,就是50維,像下圖中是兩個filter,對應的深度就是兩維
所以,這是一個新的比較小的image,它表示的是不同區域上提取到的特征,實際上不同的filter檢測的是該image同一區域上的不同特征屬性,所以每一層channel(通道)代表的是一種屬性,一塊區域有幾種不同的屬性,就有幾層不同的channel,對應的就會有幾個不同的filter對其進行convolution操作

這件事情可以repeat很多次,你可以把得到的這個比較小的image,再次進行convolution和max pooling的操作,得到一個更小的image,依次類推
有這樣一個問題:假設我第一個convolution有25個filter,通過這些filter得到25個feature map,然後repeat的時候第二個convolution也有25個filter,那這樣做完,我是不是會得到25^2個feature map?
其實不是這樣的,你這邊做完一次convolution,得到25個feature map之後再做一次convolution,還是會得到25個feature map,因為convolution在考慮input的時候,是會考慮深度的,它並不是每一個channel分開考慮,而是一次考慮所有的channel,所以,你convolution這邊有多少個filter,再次output的時候就會有多少個channel
因此你這邊有25個channel,經過含有25個filter的convolution之後output還會是25個channel,只是這邊的每一個channel,~~它都是一個cubic(立方體),它的高有25個value那麽高~~

### **Flatten**
做完convolution和max pooling之後,就是FLatten和Fully connected Feedforward network的部分
Flatten的意思是,把左邊的feature map拉直,然後把它丟進一個Fully connected Feedforward network,然後就結束了,也就是說,我們之前通過CNN提取出了image的feature,它相較於原先一整個image的vetor,少了很大一部分內容,因此需要的參數也大幅度地減少了,但最終,也還是要丟到一個Fully connected的network中去做最後的分類工作

## **CNN in Keras**
### **內容簡介**
接下來就講一下,如何用Keras來implement CNN,實際上在compile、training和fitting的部分,內容跟DNN是一模一樣的,對CNN來說,唯一需要改變的是network structure,以及input的format
本來在DNN里,input是一個由image拉直展開而成的vector,但現在如果是CNN的話,它是會考慮input image的幾何空間的,所以不能直接input一個vector,而是要input一個tensor給它(tensor就是高維的vector),這里你要給它一個三維的vector,一個image的長寬各是一維,如果它是彩色的話,RGB就是第三維,所以你要assign一個三維的matrix,這個高維的matrix就叫做tensor

那怎麽implement一個convolution的layer呢?
```!
model2.add( Convolution2D(25,3,3, input_shape=(28,28,1)) )
```
還是用```model.add```增加CNN的layer,將原先的Dense改成Convolution2D,參數25代表你有25個filter,參數3,3代表你的filter都是3*3的matrix,此外你還需要告訴model,你input的image的shape是什麽樣子的,假設我現在要做手寫數字識別,input就是28*28的image,又因為它的每一個pixel都只有單一顏色,因此input_shape的值就是(28,28,1),如果是RGB的話,1就要改成3
然後增加一層Max Pooling的layer
```!
model2.add( MaxPooling2D(2,2) )
```
這裡參數(2,2)指的是,我們把通過convolution得到的feature map,按照2*2的方式分割成一個個區域,每次選取最大的那個值,並將這些值組成一個新的比較小的image,作為subsampling的結果
### **過程描述**
+ 假設我們input是一個1*28*28的image
+ 通過25個filter的convolution layer以後你得到的output,會有25個channel,又因為filter的size是3x3,因此如果不考慮image邊緣處的處理的話,得到的channel會是26x26的,因此通過第一個convolution得到25x26x26的cubic image(這里把這張image想象成長寬為26,高為25的cubic立方體)
+ 接下來就是做Max pooling,把2x2的pixel分為一組,然後從里面選一個最大的組成新的image,大小為25x13x13(cubic長寬各被砍掉一半)
+ 再做一次convolution,假設這次選擇50個filter,每個filter size是3x3的話,output的channel就變成有50個,那13x13的image,通過3x3的filter,就會變成11*11,因此通過第二個convolution得到50x11x11的image(得到一個新的長寬為11,高為50的cubic)
+ 再做一次Max Pooling,變成50x5x5

在第一個convolution里面,每一個filter都有9個參數,它就是一個3x3的matrix;但是在第二個convolution layer里面,雖然每一個filter都是3x3,但它其實不是3x3個參數,因為它的input是一個25x13x13的cubic,這個cubic的channel有25個,所以要用同樣高度的cubic filter對它進行卷積,於是我們的filter實際上是一個25x3x3的cubic,所以這邊每個filter共有225個參數

通過兩次convolution和max pooling的組合,最終的image變成了50*5*5的size,然後使用Flatten將這個image拉直,變成一個1250維的vector,再把它丟到一個Fully Connected Feedforward network里面,network structure就搭建完成了
### **一個重要的問題**
看到這里,你可能會有一個疑惑,第二次convolution的input是25x13x13的cubic,用50個3x3的filter卷積後,得到的輸出時應該是50個cubic,且每個cubic的尺寸為25x11x11,那麽max pooling把長寬各砍掉一半後就是50層25x5x5的cubic,那flatten後不應該就是50x25x5x5嗎?
其實不是這樣的,在第二次做convolution的時候,我們是用25x3x3的cubic filter對25x13x13的cubic input進行卷積操作的,filter的每一層和input cubic中對應的每一層(也就是每一個channel),它們進行內積後,還要把cubic的25個channel的內積值進行求和,作為這個“neuron”的output,它是一個scalar,這個cubic filter對整個cubic input做完一遍卷積操作後,得到的是一層scalar,然後有50個cubic filter,對應著50層scalar,因此最終得到的output是一個50x11x11的cubic!
這里的關鍵是filter和image都是cubic,每個cubic filter有25層高,它和同樣有25層高的cubic image做卷積,並不是單單把每個cubic對應的channel進行內積,還會把這些內積求和!!!最終變為1層,==**因此兩個矩陣或者tensor做了卷積後,不管之前的維數如何,都會變為一個scalor!**==,故如果有50個Filter,無論input是什麽樣子的,最終的output還會是50層
## **Appendix:CNN in Keras2.0**
這里還是舉手寫數字識別的例子,將單純使用DNN和加上CNN的情況作為對比
### **code**
```python==
import numpy as np
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D, Flatten, Conv2D
from keras.layers.core import Dense, Dropout, Activation
from keras.optimizers import SGD, Adam
from keras.utils import np_utils
from keras.datasets import mnist
# categorical_crossentropy
def load_mnist_data(number):
# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train[0:number]
y_train = y_train[0:number]
x_train = x_train.reshape(number, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
# convert class vectors to binary class matrices
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
x_train = x_train / 255
x_test = x_test / 255
return (x_train, y_train), (x_test, y_test)
if __name__ == '__main__':
(x_train, y_train), (x_test, y_test) = load_mnist_data(10000)
# do DNN
model = Sequential()
model.add(Dense(input_dim=28 * 28, units=500, activation='relu'))
model.add(Dense(units=500, activation='relu'))
model.add(Dense(units=500, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
model.summary()
model.compile(loss='categorical_crossentropy',
optimizer='adam', metrics=['accuracy'])
model.fit(x_train, y_train, batch_size=100, epochs=20)
result_train = model.evaluate(x_train, y_train)
print('\nTrain Acc:\n', result_train[1])
result_test = model.evaluate(x_test, y_test)
print('\nTest Acc:\n', result_test[1])
# do CNN
x_train = x_train.reshape(x_train.shape[0], 1, 28, 28)
x_test = x_test.reshape(x_test.shape[0], 1, 28, 28)
model2 = Sequential()
model2.add(Conv2D(25, (3, 3), input_shape=(
1, 28, 28), data_format='channels_first'))
model2.add(MaxPooling2D((2, 2)))
model2.add(Conv2D(50, (3, 3)))
model2.add(MaxPooling2D((2, 2)))
model2.add(Flatten())
model2.add(Dense(units=100, activation='relu'))
model2.add(Dense(units=10, activation='softmax'))
model2.summary()
model2.compile(loss='categorical_crossentropy',
optimizer='adam', metrics=['accuracy'])
model2.fit(x_train, y_train, batch_size=100, epochs=20)
result_train = model2.evaluate(x_train, y_train)
print('\nTrain CNN Acc:\n', result_train[1])
result_test = model2.evaluate(x_test, y_test)
print('\nTest CNN Acc:\n', result_test[1])
```
### **result**
#### **DNN**
```python=
Using TensorFlow backend.
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 500) 392500
_________________________________________________________________
dense_2 (Dense) (None, 500) 250500
_________________________________________________________________
dense_3 (Dense) (None, 500) 250500
_________________________________________________________________
dense_4 (Dense) (None, 10) 5010
=================================================================
Total params: 898,510
Trainable params: 898,510
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
10000/10000 [==============================] - 2s 207us/step - loss: 0.4727 - acc: 0.8643
Epoch 2/20
10000/10000 [==============================] - 1s 149us/step - loss: 0.1613 - acc: 0.9521
Epoch 3/20
10000/10000 [==============================] - 2s 159us/step - loss: 0.0916 - acc: 0.9726
Epoch 4/20
10000/10000 [==============================] - 2s 173us/step - loss: 0.0680 - acc: 0.9769
Epoch 5/20
10000/10000 [==============================] - 2s 166us/step - loss: 0.0437 - acc: 0.9850
Epoch 6/20
10000/10000 [==============================] - 2s 166us/step - loss: 0.0274 - acc: 0.9921
Epoch 7/20
10000/10000 [==============================] - 2s 168us/step - loss: 0.0265 - acc: 0.9892
Epoch 8/20
10000/10000 [==============================] - 2s 161us/step - loss: 0.0240 - acc: 0.9916
Epoch 9/20
10000/10000 [==============================] - 2s 169us/step - loss: 0.0149 - acc: 0.9950
Epoch 10/20
10000/10000 [==============================] - 2s 155us/step - loss: 0.0258 - acc: 0.9933
Epoch 11/20
10000/10000 [==============================] - 2s 168us/step - loss: 0.0206 - acc: 0.9934
Epoch 12/20
10000/10000 [==============================] - 2s 161us/step - loss: 0.0132 - acc: 0.9955
Epoch 13/20
10000/10000 [==============================] - 2s 168us/step - loss: 0.0113 - acc: 0.9964
Epoch 14/20
10000/10000 [==============================] - 2s 169us/step - loss: 0.0027 - acc: 0.9991
Epoch 15/20
10000/10000 [==============================] - 2s 157us/step - loss: 6.6533e-04 - acc: 0.9999
Epoch 16/20
10000/10000 [==============================] - 1s 150us/step - loss: 1.1253e-04 - acc: 1.0000
Epoch 17/20
10000/10000 [==============================] - 2s 152us/step - loss: 8.3190e-05 - acc: 1.0000
Epoch 18/20
10000/10000 [==============================] - 2s 174us/step - loss: 6.7850e-05 - acc: 1.0000
Epoch 19/20
10000/10000 [==============================] - 2s 173us/step - loss: 5.6810e-05 - acc: 1.0000
Epoch 20/20
10000/10000 [==============================] - 2s 172us/step - loss: 4.8757e-05 - acc: 1.0000
10000/10000 [==============================] - 1s 97us/step
Train Acc: 1.0
10000/10000 [==============================] - 1s 77us/step
Test Acc: 0.9661
```
#### **CNN**
```python=
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 25, 26, 26) 250
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 13, 26) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 10, 11, 50) 11750
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 5, 5, 50) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 1250) 0
_________________________________________________________________
dense_5 (Dense) (None, 100) 125100
_________________________________________________________________
dense_6 (Dense) (None, 10) 1010
=================================================================
Total params: 138,110
Trainable params: 138,110
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
10000/10000 [==============================] - 8s 785us/step - loss: 0.6778 - acc: 0.8113
Epoch 2/20
10000/10000 [==============================] - 7s 734us/step - loss: 0.2302 - acc: 0.9349
Epoch 3/20
10000/10000 [==============================] - 8s 765us/step - loss: 0.1562 - acc: 0.9532
Epoch 4/20
10000/10000 [==============================] - 8s 760us/step - loss: 0.1094 - acc: 0.9680
Epoch 5/20
10000/10000 [==============================] - 8s 843us/step - loss: 0.0809 - acc: 0.9763
Epoch 6/20
10000/10000 [==============================] - 7s 748us/step - loss: 0.0664 - acc: 0.9810
Epoch 7/20
10000/10000 [==============================] - 8s 764us/step - loss: 0.0529 - acc: 0.9832
Epoch 8/20
10000/10000 [==============================] - 7s 747us/step - loss: 0.0370 - acc: 0.9904
Epoch 9/20
10000/10000 [==============================] - 7s 687us/step - loss: 0.0302 - acc: 0.9919
Epoch 10/20
10000/10000 [==============================] - 7s 690us/step - loss: 0.0224 - acc: 0.9940
Epoch 11/20
10000/10000 [==============================] - 7s 698us/step - loss: 0.0177 - acc: 0.9959
Epoch 12/20
10000/10000 [==============================] - 7s 690us/step - loss: 0.0154 - acc: 0.9965
Epoch 13/20
10000/10000 [==============================] - 7s 692us/step - loss: 0.0126 - acc: 0.9962
Epoch 14/20
10000/10000 [==============================] - 7s 689us/step - loss: 0.0130 - acc: 0.9966
Epoch 15/20
10000/10000 [==============================] - 7s 691us/step - loss: 0.0092 - acc: 0.9977
Epoch 16/20
10000/10000 [==============================] - 7s 691us/step - loss: 0.0067 - acc: 0.9986
Epoch 17/20
10000/10000 [==============================] - 7s 687us/step - loss: 0.0069 - acc: 0.9985
Epoch 18/20
10000/10000 [==============================] - 7s 691us/step - loss: 0.0040 - acc: 0.9995
Epoch 19/20
10000/10000 [==============================] - 7s 745us/step - loss: 0.0020 - acc: 1.0000
Epoch 20/20
10000/10000 [==============================] - 8s 782us/step - loss: 0.0014 - acc: 1.0000
10000/10000 [==============================] - 7s 657us/step
Train CNN Acc: 1.0
10000/10000 [==============================] - 5s 526us/step
Test CNN Acc: 0.98
```