2021年11月20日 星期六

減少卷積神經網路的層數,參數量就一定會減少嗎?答案可能相反!

標題:減少卷積神經網路的層數,參數量就一定會減少嗎?答案可能相反!


前言:

在建置影像的卷積神經網路模型時,過度配適(過擬合,overfitting)的現象常常在樣本數不足的情況出現,除了想辦法透過資料擴增遷移學習的方法解決這個問題外,神經網路的模型參數量過大往往是更容易造成此問題的重要因素。

但是,不少初學者透過減少"卷積層及池化層"的方式減少模型的大小,但是這個動作在"某些情況"反而造成模型的參數量更大

本文透過實際計算參數量的方式,講述"減少卷積層及池化層"時"參數量反而越來越大"的情況。


-----------分隔線-----------

卷積運算的介紹:

卷積運算相當於濾鏡運算,簡單來說,我們用下圖的例子作為範例。

這是常見的卷積運算,但如果是有3個色板的RGB影像,該如何計算呢,請見下圖。


三個色板,進行了三種不同的卷積核(kernel)的運算後,加在一起,這時,神經網路需要更新的參數量為27(3個色板乘上3×3的kernel)個,也就是說,無論影像多大,卷積層的參數量都不變

如果第一層卷積層的輸出為32個色板,那麼,上面這張圖的運算就會有32次,再將這32個輸出合併為同一個張量的32個色板,不過這32次卷積運算中,參數的數值會不同。

在這裡,參數量為3×(3×3)×32=864(與圖片大小無關)

如果第二層卷積層的輸出為64個色板,那麼,就可以想像成,上面那張圖的左邊有32個4×4大小的圖片,再乘上3×3大小的核(kernel),由於這個動作只會輸出一個色板,所以類似此運算會做64次。

在這裡,參數量為32×(3×3)×64=18,432(與圖片大小無關)


看到這裡,可能會覺得,卷積神經網路其實是在進行3D的卷積運算(像下圖這樣),其實在實作時通常還是2D的運算。(見更下一張圖)

程式庫在進行運算時,通常會將3D的輸入轉換成2D的輸入(如下圖),這樣做的好處,就是能利用線性代數的程式庫陣列的計算最佳化


像下圖這樣,將3D的輸入和核都轉成二維矩陣時,就可以透過2維的乘法運算,快速完成3D的卷積運算,算完後再轉換回3D的大小



然而,為什麼減少神經網路的層數時在部分情況反而會造成模型的參數量更大呢,原因就出在平坦層後方的全連結層


-----------分隔線-----------


全連結層的矩陣乘積運算:

先看下圖,你可以看到,全連結層的運算模擬圖(輸入的大小為2、隱藏層的神經元大小為3)。

在這裡,看起來,隱藏層的參數量就是6個(不考慮偏權值)。



實際上,它就是在進行一種矩陣相乘的運算,如下圖。
這就是常見到的,Y=X×W+B。


所以很容易就能看出來,W的數量並不是神經元數量,而是線的數量

那麼,如果平坦化後的結果是32,768個數字,在下一層的全連結層的輸出有128個神經元時,就會有32,768×128= 4,194,304個參數

這一層的參數量遠遠比前面所有的卷積運算的參數量加起來都還大。

而這一層的參數量,跟圖片的大小有間接關係。

-----------分隔線-----------

現在來以例子來計算吧。

圖片的輸入為224*224的大小,有RGB三個色板。

模型為:

input→32個filter的3×3濾鏡→2×2池化層→64個filter的3×3濾鏡→2×2池化層→128個filter的3×3濾鏡→2×2池化層→256個filter的3×3濾鏡→2×2池化層→平坦層→128個神經元的全連結層→2個神經元的輸出層。

然後,為了縮減模型的大小,弄巧成拙減少了一層卷積層和池化層

input→32個filter的3×3濾鏡→2×2池化層→64個filter的3×3濾鏡→2×2池化層→128個filter的3×3濾鏡→2×2池化層→平坦層→128個神經元的全連結層→2個神經元的輸出層。

兩者的差異如下(不考慮偏權值)

第一個模型的參數量(六百萬個參數):

3×(3×3)×32→0→32×(3×3)×64→0→64×(3×3)×128→0→128×(3×3)×256→0→0→[14×14×256]×128→128×2=864+18,432+73,728+294,912+[6,422,528]+256=6,810,720。

第二個弄巧成拙的模型參數量(一千兩百萬個參數):

3×(3×3)×32→0→32×(3×3)×64→0→64×(3×3)×128→0→0→[28×28×128]×128→128×2=864+18,432+73,728+[12,845,056]+256=12,938,336。

由上述計算可算出,減少了一層卷積層和池化層後,由於能縮減四分之三參數的池化層減少了少了池化層後,圖片的大小14×14變成28×28,接上全連結層後,參數量反而增加了

所以,如果要減少模型的大小,不妨先注意平坦層後方的全連結層。

另外,使用全域平均池化,可以將所有色板直接取平均值,只留下一個數值,這樣的話是能顯著減少那一層的參數量的,這是一個好方法。


真棒,今天又學到了很多東西呢!

2021年11月5日 星期五

C# tabControl停駐於父容器

標題:如何將C#的tabControl停駐於父容器

副標題:如何將C#的控制項停駐於父容器


今天使用C#的tabControl時,發現視窗大小改變後,tabControl控制項將無法跟著改變(會出現空隙)。



怎麼辦呢,首先,去tabControl的控制項屬性,找到Dock的屬性後,把None的選項改成Fill(選中間的矩形區域)


選好後,該控制項就能隨著父容器或視窗大小改變了。


2021年10月3日 星期日

解決"CV_BGR2RGB"未被宣告的問題

標題:解決"CV_BGR2RGB"未被宣告的問題


今天使用OpenCV4的時候,發現報錯了,"CV_BGR2RGB"居然不能用。



cvtColor(img, img, CV_BGR2RGB);//這行錯了

原因是因為OpenCV4改用"COLOR_BGR2RGB"而不是"CV_BGR2RGB"了。


所以


只要把它改成"COLOR_BGR2RGB"後,就可以執行了。


cvtColor(img, img, COLOR_BGR2RGB);//這行可以執行



參考連結:https://github.com/NVIDIA/DALI/issues/735

2021年10月1日 星期五

tensorflow logging.h編譯失敗的解法

標題:tensorflow logging.h編譯失敗的解法


今天編譯tensorflow C++的時候,發現logging.h無法編譯(編譯失敗),如圖:



那個叫「TF_DEFINE_CHECK_OP_IMPL」的東西總是給紅底線的報錯。


如何解決呢。

去專案屬性的地方,在C/C++/前置處理器/前置處理器定義的地方,新增「NOMINMAX」。


新增完成後套用確定。

接著回到code的地方,會看見「TF_DEFINE_CHECK_OP_IMPL」的紅底線已經消失,而且已經可以編譯了。


2021年9月30日 星期四

在Visual Studio安裝TensorFlow C語言環境

標題:在Visual Studio 2019安裝Tensorflow C語言環境

搜尋標題:Visual Studio 2019 install Tensorflow for C


今天要來用Visual Studio 2019執行C語言版本的Tensorflow。

這個教學給的範例應該不難,有問題再留言問我。


直接進入正題吧!


首先,點進去這個網址:

https://www.tensorflow.org/install/lang_c


然後依照作業系統和CPU/GPU等需求下載檔案。



然後會下載到一個檔名類似「libtensorflow-gpu-windows-x86_64-2.6.0.zip」的檔案,把它解壓縮。


解壓縮後把資料夾貼到C磁碟機或你想要的地方。

我們會看到資料夾中有個include資料夾和lib資料夾。

這兩個資料夾的名稱,要記好,等一下會使用好幾次。



然後開啟Visual Studio,建立C++版本的主控台應用程式。


建立好後按專案/<專案名稱>屬性。



在VC++目錄的「include目錄」加上include資料夾的路徑。

我的範例是「C:\libtensorflow-gpu-windows-x86_64-2.6.0\include」

在VC++目錄的「程式庫目錄」加上lib資料夾的路徑。

我的範例是「C:\libtensorflow-gpu-windows-x86_64-2.6.0\lib」



然後去連接器/輸入/其他相依性 的地方,輸入tensorflow.lib。

「tensorflow.lib」存在於lib資料夾。

都設定完成後按套用確定。



然後輸入以下程式碼(可以直接複製):

#include <iostream>
#include <stdio.h>
#include <tensorflow/c/c_api.h> 
using namespace std;
int main()
{
    printf("Hello from TensorFlow C library version %s\n"TF_Version());
    int a;
    cin >> a;
}

如圖:


這段程式是要把TensorFlow的版本print出來的,可用來確認有正確使用到TensorFlow。
然後cin是為了防止TensorFlow版本印出來後發生視窗立即閃退的情況。

輸入完程式碼後建置程式:



接著去專案中的「Debug/Release」資料夾開啟該程式。

可能會出現「找不到tensorflow.dll」之類的錯誤。


沒關係,我們去剛才解壓縮後的lib資料夾,找到「tensorflow.dll」檔案,複製到跟執行檔相同資料夾位置的地方。


複製dll檔過去後,再次啟動程式,就可以讀取到TensorFlow了。


顯示出來的,就是TensorFlow的版本(我的版本是2.6.0),如果看見TensorFlow的版本被顯示出來了,代表TensorFlow的環境建置成功了喔。

2021年9月20日 星期一

建立token並push程式到github上

標題:[教學]建立token並push程式到github上(2021/09)

完整標題:[教學]建立access token並push程式到github上(2021/09)


最近使用git push新版本的程式到github上時,卻發現不能push。


原來,現在github都要求要用access token登入才能push程式到github上了。原來的密碼不再被允許push。


現在來建立一個access token吧。

首先,進入github,到右邊的地方點自己的頭貼,然後按「Settings」。


按左邊的「Developer settings」


按左邊的「Personal access tokens」,然後按右邊的「Generate new token」


輸入Note(可隨意輸入),然後選擇日期,並勾選「repo」、「admin:repo_hook」等選項


按「Generate token」


然後就會看到你的「Personal access tokens」被建立了,把它複製起來,並保存下來(它只會顯示一次)。


回到git介面,再次push程式。

它會請求輸入github帳號,就把帳號輸入進去。



然後會要求輸入密碼,就貼上剛剛複製的「Personal access tokens」就好。


按下「OK」之後,就可以成功push了。


2021年7月19日 星期一

驗證碼總是輸入錯誤的解決方法

標題:驗證碼總是輸入錯誤的解決方法


點進來這個頁面的,想必都是因為在網頁上輸入的驗證碼總是錯誤,而且試到沒路了吧。

如果你的電腦有裝卡巴斯基防毒,那這個驗證碼錯誤就非常有可能是卡巴斯基防毒造成的問題了,以下將提供解法。


驗證碼錯誤的情況:

像這個陽明交大的網站,輸入任何驗證碼都錯,原因是因為卡巴斯基防毒搞得鬼。


解決方法:

首先進入卡巴斯基防毒軟體介面,按左下角的設定圖案。



然後看見有個「注入指令碼到網頁流量從而與網頁互動」的選項。

把這個垃圾選項給取消勾選。



儲存設定後,重開瀏覽器,驗證碼就可以輸入了。

2021年6月29日 星期二

[教學]將Win10中文輸入法預設為英文

標題:[教學]將Winows10的中文輸入法預設為英文

[教學]將Win10的中文輸入法預設為英文

本篇文章網址:https://corettainformation.blogspot.com/2021/06/win10.html


常常用Windows10用cmd、改檔案名稱或開軟體存檔時,想打的是英文,但發現打出來的是中文,很麻煩,所以今天就來將預設的中文輸入法改成英文。

就不用每次都要按shift了。


操作步驟:

1.開啟控制台,選擇時鐘和區域


2.選擇地區


3.按語言喜好設定


4.慣用語言的地方有個「中文(繁體、台灣)」,按它下方的選項。


5.按微軟注音下方的選項


6.選擇「一般」


7.預設輸入模式從中文改成英數字元



改好後,每當開啟應用程式或改檔名時,預設的輸入模式就是英文了。

想打中文時,按一下shift就可以了。

2021年6月28日 星期一

numpy的操作示範(待更新)

 #引入numpy套件

import numpy as np

 

#將串列轉成ndarray陣列

a=np.array([1,2,3,4,5,6,7,8,9,10,11,12])


#印出陣列的形狀

print(a.shape)

#(12,)

 

#印出a的內容

print(a)

#[ 1  2  3  4  5  6  7  8  9 10 11 12]

 

#將a轉成3*4的二維陣列

a.shape=3,4


#印出a的內容

print(a)

'''

[[ 1  2  3  4]

 [ 5  6  7  8]

 [ 9 10 11 12]]

'''

 

#將a轉成2*6的二維陣列

a=a.reshape(2,6)


#印出a的內容

print(a)

'''

[[ 1  2  3  4  5  6]

 [ 7  8  9 10 11 12]]

'''


#指定第[0,5]的數字為33

a[0,5]=33

print(a)

'''

[[ 1  2  3  4  5 33]

 [ 7  8  9 10 11 12]]

'''


#指定第[1,5]的數字為66

a[1,5]=66

print(a)

'''

[[ 1  2  3  4  5 33]

 [ 7  8  9 10 11 66]]

'''

 

#印出a的最大值、最小值、中位數

print(a.max(),a.min(),np.median(a))

#66 1 7.5

 

#矩陣運算,將a的所有數字加上5後乘以2

a=(a+5)*2

 

print(a)

'''

[[ 12  14  16  18  20  76]

 [ 24  26  28  30  32 142]]

'''

 

#印出有哪些數字大於30

print(a>30)

'''

[[False False False False False  True]

 [False False False False  True  True]]

'''

 

#印出大於30的數字

print(a[a>30])

#[ 76  32 142]

 

#將大於30的數字歸零

a[a>30]=0

print(a)

 

#將a由小到大排序

a=np.sort(a)

print(a)

'''

[[ 0 12 14 16 18 20]

 [ 0  0 24 26 28 30]]

'''



#建立0到59,間隔為10的陣列,並指派到b

b=np.arange(0,60,10)

print(b)

#[ 0 10 20 30 40 50]

 

#將一維陣列改成為二維陣列

b=b.reshape(-1,1)

print(b)

'''

[[ 0]

 [10]

 [20]

 [30]

 [40]

 [50]]

'''

 

#將上方的二維陣列加上[0, 1, 2, 3, 4, 5]

b=np.arange(0,60,10).reshape(-1,1)+np.arange(0,6)

print(b)

'''

[[ 0  1  2  3  4  5]

 [10 11 12 13 14 15]

 [20 21 22 23 24 25]

 [30 31 32 33 34 35]

 [40 41 42 43 44 45]

 [50 51 52 53 54 55]]

'''

 

#範例:若沒有轉成二維陣列就相加的情況

temp=np.arange(0,60,10)+np.arange(0,6)

print(temp)

#[ 0 11 22 33 44 55]

 

#選取中間(去除掉外框的部分)

print(b[1:-1,1:-1])

'''

[[11 12 13 14]

 [21 22 23 24]

 [31 32 33 34]

 [41 42 43 44]]

'''

#將中間歸零

b[1:-1,1:-1]=0

print(b)

'''

[[ 0  1  2  3  4  5]

 [10  0  0  0  0 15]

 [20  0  0  0  0 25]

 [30  0  0  0  0 35]

 [40  0  0  0  0 45]

 [50 51 52 53 54 55]]

'''


#待更新

[教學]在QT中新增菜單

 標題:[教學]在QT中新增菜單

本篇文章網址:https://corettainformation.blogspot.com/2021/06/qt.html


//首先,建立一條MenuBar

//調用QMainWidow的成員函數menuBar後,只要將表單新增進去就能添加到QMainWidow中。

QMenuBar* mb = menuBar();

//接著新增一個Menu菜單,上面寫檔案

QMenu* menu = new QMenu("檔案(&F)");

//建立菜單的第二層,上面寫New

QAction* action = new QAction("New", NULL);

//將第二層菜單加入第一層菜單項中

menu->addAction(action); 

//綁定按下後要執行的函數(kaleido_painter::open),這行可省略或自行更改。

connect(action, &QAction::triggered, this, &kaleido_painter::open);

 //將菜單項加入MenuBar中

mb->addMenu(menu); 


結果如下:




在QT的connect傳入函數的參數

標題:在QT的connect傳入函數的參數

關鍵字:QT、connect、parameter

本篇文章網址:https://corettainformation.blogspot.com/2021/06/qtconnect.html


今天使用C++寫QT程式時,想要讓QT按下清單時可以執行showSecondary_Window函數,然後又要傳mosaic_Window的參數進去。


本來程式碼是長這個樣子的:

action = new QAction("馬賽克", NULL);

menu->addAction(action); 

connect(action, &QAction::triggered, this, &kaleido_painter::showSecondary_Window);

它可以呼叫showSecondary_Window函數,但是不能傳入任何參數。


怎麼辦呢?

只要把connect函數改成下方的格式就可以了:

connect(action, &QAction::triggered, this, [=]() {

kaleido_painter::showSecondary_Window(mosaic_Window);

});


這樣就能同時呼叫showSecondary_Window函數又能傳mosaic_Window的參數進去

2021年6月26日 星期六

"亞型"在醫學上代表什麼意思?

標題:"亞型"在醫學上代表什麼意思?


在一些維基百科的醫學相關條目中,有時會看見"亞型"這個詞彙,像是Omicron是亞型病毒株、HIV-2是HIV病毒的亞型、亞斯伯格症是自閉症的亞型、費爾蒂綜合症被認為是類風濕關節炎的亞型,等等...


因為亞型這個詞彙比較少出現,所以我到近日才稍微搞懂亞型的意思。


簡單來說亞型就是一種東西的不同類型,像是A疾病的亞型跟A疾病是同一種疾病,但是又有分別,為了區分,就稱作是A疾病的亞型。


參考資料:https://www.zhihu.com/question/29552951

2021年6月21日 星期一

Google屢次停止運作,臨時解決方法

 標題:「Google屢次停止運作」的臨時解決方法


今天手機打開來發現,手機一直跳出訊息:

「Google屢次停止運作」



這邊提供一個暫時性的解決方式。

首先,點選設定,按應用程式與通知。



按應用程式資訊。



找到Google的選項。


按停用。


按停用應用程式。



看到恢復原廠的通知,就按取消



然後,「Google屢次停止運作」就暫時不會出現了。

接下來,就先慢慢等Google的更新修好它吧。


等Google修好後再啟用Google,就能繼續使用Google的服務。