# **Machine Learning Class11 (Part 1)** # **Convolutional Neural network (CNN)** > CNN常常被用在影像處理上,它的theory base就是三個property(屬性),和兩個架構 convolution 架構:針對property 1和property 2 max pooling架構:針對property 3 ![](https://i.imgur.com/mq1vQhC.jpg) ## **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等等 ![](https://i.imgur.com/7sbUA5e.png) 那現在的問題是這樣子:**當我們直接用一般的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的一小部分,就可以決定這件事情了 ![](https://i.imgur.com/kecnjGX.png) 舉例來說,假設現在我們有一張鳥的圖片,那第一層hidden layer的某一個neuron的工作是,檢測有沒有鳥嘴的存在(你可能還有一些neuron偵測有沒有鳥嘴的存在、有一些neuron偵測有沒有爪子的存在、有一些neuron偵測有沒有翅膀的存在、有沒有尾巴的存在,之後合起來,就可以偵測,圖片中有沒有一只鳥),那它其實並不需要看整張圖,因為,其實我們只要給neuron看這個小的紅色杠杠里面的區域,它其實就可以知道說,這是不是一個鳥嘴,對人來說也是一樣,只要看這個小的區域你就會知道說這是鳥嘴,所以,**每一個neuron其實只要連接到一個小塊的區域就好,它不需要連接到整張完整的圖,因此也對應著更少的參數** #### **The same patterns appear in different regions** 同樣的pattern,可能會出現在image的不同部分,但是它們有同樣的形狀、代表的是同樣的含義,因此它們也可以用同樣的neuron、同樣的參數,被同一個detector檢測出來 ![](https://i.imgur.com/HgbUftl.png) 舉例來說,上圖中分別有一個處於左上角的鳥嘴和一個處於中央的鳥嘴,但你並不需要訓練兩個不同的detector去專門偵測左上角有沒有鳥嘴和中央有沒有鳥嘴這兩件事情,這樣做太冗余了,我們要cost down(降低成本),我們並不需要有兩個neuron、兩組不同的參數來做duplicate(重覆一樣)的事情,所以**我們可以要求這些功能幾乎一致的neuron共用一組參數,它們share同一組參數就可以幫助減少總參數的量** #### **Subsampling the pixels will not change the object** 我們可以對一張image做subsampling(二次抽樣),假如你把它奇數行、偶數列的pixel拿掉,image就可以變成原來的十分之一大小,而且並不會影響人對這張image的理解,對你來說,下面兩張大小不一的image看起來不會有什麽太大的區別,你都可以識別里面有什麽物件,因此subsampling對圖像辨識來說,可能是沒有太大的影響的 ![](https://i.imgur.com/MlmWL5d.png) 所以,**我們可以利用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里面去,最終得到影像辨識的結果 ![](https://i.imgur.com/fpA1gSC.png) 我們基於之前提到的三個對影像處理的觀察,設計了CNN這樣的架構,第一個是要偵測一個pattern,你不需要看整張image,只要看image的一個小部分;第二個是同樣的pattern會出現在一張圖片的不同區域;第三個是我們可以對整張image做subsampling 那**前面這兩個property,是用convolution的layer來處理的;最後這個property,是用max pooling來處理的** ![](https://i.imgur.com/mq1vQhC.jpg) ### **Convolution** 假設現在我們network的input是一張6x6的image,圖像是黑白的,因此每個pixel只需要用一個value來表示,而在convolution layer里面,有一堆Filter,這邊的每一個Filter,其實就等同於是Fully connected layer里的一個neuron #### **Property 1** ![](https://i.imgur.com/njOHtmM.png) 每一個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 ![](https://i.imgur.com/X4LPWT4.png) 觀察上圖中的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 ![](https://i.imgur.com/4DTG2L0.png) 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,要怎麽做呢? ![](https://i.imgur.com/rI47u7B.png) 這個時候你的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 ![](https://i.imgur.com/C6WaRoR.png) 接下來我們來解釋這件事情: 如下圖所示,我們在做convolution的時候,把filter放在image的左上角,然後再去做inner product,得到一個值3;這件事情等同於,我們現在把這個image的6*6的matrix拉直變成右邊這個用於input的vector,然後,你有一個紅色的neuron,這些input經過這個neuron之後,得到的output是3 ![](https://i.imgur.com/RIUuNX8.png) ##### **每個“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 ![](https://i.imgur.com/grxVp6d.png) 你會发現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縮小! ![](https://i.imgur.com/JT7PjbA.png) 講到這里你可能會有一個問題,如果取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操作 ![](https://i.imgur.com/bF2zk48.png) 這件事情可以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那麽高~~ ![](https://i.imgur.com/Ap2Cd7d.png) ### **Flatten** 做完convolution和max pooling之後,就是FLatten和Fully connected Feedforward network的部分 Flatten的意思是,把左邊的feature map拉直,然後把它丟進一個Fully connected Feedforward network,然後就結束了,也就是說,我們之前通過CNN提取出了image的feature,它相較於原先一整個image的vetor,少了很大一部分內容,因此需要的參數也大幅度地減少了,但最終,也還是要丟到一個Fully connected的network中去做最後的分類工作 ![](https://i.imgur.com/Ic8b6X2.png) ## **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 ![](https://i.imgur.com/YWvQ2Ue.png) 那怎麽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 ![](https://i.imgur.com/ApJCXnM.png) 在第一個convolution里面,每一個filter都有9個參數,它就是一個3x3的matrix;但是在第二個convolution layer里面,雖然每一個filter都是3x3,但它其實不是3x3個參數,因為它的input是一個25x13x13的cubic,這個cubic的channel有25個,所以要用同樣高度的cubic filter對它進行卷積,於是我們的filter實際上是一個25x3x3的cubic,所以這邊每個filter共有225個參數 ![](https://i.imgur.com/Gz2NweC.png) 通過兩次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 ```