作者:賴桑
接續第一集介紹了 AI 的歷史脈絡,本篇將來教大家什麼是 NN 與 ANN ?同時還會使用Arduino Nano 跑一個 ANN 小案例,手把手帶你把PC做出 ANN 的成果,轉移到 Arduino Nano 去執行。
到底有多少 NN ?
上一篇文提到神經網路 NN ,根據目前常見的應用,大概有幾個是常見的:「 ANN 、 kNN 、 CNN、 RNN 、 R-CNN 。」那這些 NN 又是什麼?怎麼用呢?其實,每一種 NN 會根據其設計上的特性跟用途,而有適用的範圍,其實就跟醫師會對症下藥的道理一樣。因此該採用哪種做哪個功能,最簡單的方法,就是直接試試看。
最老式結構:ANN
ANN 的全名叫做 Artificial neural networks ,有些教科書稱 connectionist systems ,事實上…還是那句老話:模仿動物的腦結構,ANN的構型就類似這張圖:
Input 、 Output 顧名思義就是輸入與輸出部份,而 Hidden 就是在實際進行運算的部分,這部分根據之前的文章便能看懂,實際上就是一堆數據,不然就是一堆函數; Hidden 到底可以有幾個?以上次的CNN 為例,CNN能分好幾層,每一層還很多個。
其實根本沒有固定答案,不管 Hidden 分幾層、每層要幾個,這兩項都只有四個大字「適量就好」可以描述。為什麼?根據上圖,每一個都是個數值或是函數,這是不是每一個 Hidden 的神經元都需要記憶體空間?那越多則記憶體消耗空間越多。另外,分那麼多層,一層算過又推到下一層,那等於執行所需要的步驟越多,執行步驟越多,跑起來應該是越慢吧?
不論哪一種 NN , Hidden 層的部分,越小、越少那表示對電腦的資源需求越低,這也就是為什麼現在連一些微控制器 Micro-controller 都可以做到以往在 PC 上才能跑的 NN 所達到的效果。可是話又說回來, ANN 到底可以做什麼?以下就來跑個小案例看看。
ANN 案例實作
上一篇提到麻省理工學院明斯基教授,曾經發表一篇報導,提到所謂 Perceptron 連組合邏輯數位電路 XOR 都辦不到,那我們就來讓一個八位元微控制器來試試,打臉明斯基教授一下。
首先,可能有些大大不是電機電子背景的,其實關於 XOR呢 ,數位電路的邏輯運算; XOR 的全名叫做「互斥或」 exclusive or ,它的意思就是:當兩個為一樣的話,就輸出 0 ,它的真值表 Truth table會像這樣:
輸入A | 輸入B | 輸出 |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
所以,我們就來發揮一下 Maker 精神,拿 Arduino Nano 這種小開發板,上面只有 8 bits 微控制器的來試試,可是不少大大就立刻反應了:辦得到嗎?山不轉路轉,仔細想想,那些 Hidden 層的各個神經元,有規定一定要放在同一台電腦,或者同一個微控制器嗎?並沒有吧。
那既然如此, PC 來做出 ANN 的成果,再把成果轉移到 Arduino Nano 去執行,不就好了嗎?我們先用 Python 寫 PC 上可以執行的程式, PC 上的程式執行 ANN 的訓練成果,再把訓練成果利用 Arduino IDE 搬到 Arduino Nano 上執行。首先,這得讓大家動動手,先參考怎麼安裝 NumPy 這個 package ,直接在命令提示字元的視窗裡輸入 pip install numpy 就會搞定, PC 上的程式原始碼是這樣:
import numpy as np # Times to run epoch = 10000 # There are 2 inputs inputLayerSize = 2 # NN nodes hiddenLayerSize = 3 # Only one output outputLayerSize = 1 L=0.1 # There are 2 inputs for XOR X = np.array( [ [0,0], [0,1], [1,0], [1,1] ] ) # The truth table of XOR # ANN just can learn from truly examples!!! # (adjust the weight and bias to make output is getting close to target by input) Y = np.array( [ [0], [1], [1], [0] ] ) def sigmod(x): return 1 / (1 + np.exp(-x)) def sigmoid_deriv(x): return x * (1 - x) Wh = np.random.uniform(size=(inputLayerSize, hiddenLayerSize)) Wz = np.random.uniform(size=(hiddenLayerSize, outputLayerSize)) for i in range(epoch): H = sigmod(np.dot(X, Wh)) Z = np.dot(H, Wz) E = Y - Z dZ = E * L Wz += H.T.dot(dZ) dH = dZ.dot(Wz.T) * sigmoid_deriv(H) Wh += X.T.dot(dH) print("**************** error ****************") print(E) print("***************** output **************") print(Z) print("*************** weights ***************") print("input to hidden layer weights: ") print(Wh) print("hidden to output layer weights: ") print(Wz)
如果不是 Copy Paste 的大大,應該看懂了吧?這程式沒多大,說穿了 X = np.array ([ [0,0], [0,1], [1,0], [1,1] ] )這一列就是在描述上面那個真值表,有關A、B兩個輸入。而 Y = np.array ([ [0], [1], [1], [0] ] ) 這一列就是真值表裏面的輸出一欄,那 hiddenLayerSize = 3 就是 Hidden 層裡面含有三個神經元,當然你可以自己改看看。
整個程式跑起來,就是根據這些條件,用已知的 Input 對 Output 去─湊答案,把答案湊到可以接受的情況後,就是 Hidden 層的結果(訓練的成果,也就是很多期刊文獻寫的規則 Rules );總之,記得所謂深度學習的程式設計,跟以往我們寫程式的不同在於:
這程式執行起來會出現這樣的結果,也就是等等要轉給 Arduino Nano 的數據:
接著, Arduino Nano 只要針對規則的部分執行就好!這就是 Arduino 部分的程式檔案:
#include <math.h> #define B1 4 #define B2 7 #define LED 13 #define PERCEPTRON 3 int X[1][2] = { {1,0} }; /*these matrices was calculated by python */ float W1[2][PERCEPTRON] = { {0.38492545, 3.94078829, 4.24865286}, {3.83936678, 0.20450267, 4.27767947} }; float W2[PERCEPTRON][1] = { {-2.6730679}, {-2.3817902}, {4.99647104} }; float Wo1[1][PERCEPTRON]; float sum = 0; float Y = 0; /*sigmoid function*/ float sigmoid (float x) { return 1/(1 + exp(-x)); } void setup() { Serial.begin(115200); pinMode(B1, INPUT); pinMode(B2, INPUT); pinMode(LED, OUTPUT); digitalWrite(LED, LOW); } void loop() { X[0][0] = digitalRead(B1); X[0][1] = digitalRead(B2); Serial.print("Button 1:"); Serial.print(X[0][0]); Serial.print("\tButton 2:"); Serial.println(X[0][1]); /* calculate forward part based on weights */ //hidden layer for(int i=0; i<1; i++) { for(int j=0;j <PERCEPTRON; j++) { for(int k=0; k<2; k++) { sum += X[i][k]*W1[k][j]; } Wo1[i][j] = sigmoid(sum); sum = 0; } } //output layer for(int i=0; i<1; i++) { for(int j=0;j <1; j++) { for(int k=0; k<PERCEPTRON; k++) { Y += Wo1[i][k]*W2[k][j]; } } } Serial.println(Y, 2); Y = round(Y); if (int(Y) == 1) Serial.println("---- Should be... ON ----"); else Serial.println("---- Should be... OFF ----"); digitalWrite(LED, int(Y)); Y = 0; delay(2000); } <!--stackedit_data: eyJoaXN0b3J5IjpbLTk3MDAwMzY4M119 -->
另外,要先把剛剛 Python 執行的成果複製給 Arduino Nano ,像是這樣:
之後在 Pin 4、7 各透過 10k 固定電阻,各外掛一個按鈕,就可以編譯後試試看,詳細可以看這影片:
電機系的大大們一定說:「用 TTL 就好啦!」,發揮一下想像力,如果說拿來做預測呢?我一直跑 ANN 訓練出規則,固定以後透過改變輸入的項目內容,那輸出…就應該會跟著變動囉?這樣不就可以預測有或沒有的效果了嗎?
像是這篇「 PM2.5 Forecasting Based on Artificial Neural Network and Genetic Algorithm 」期刊,就是利用 ANN 當作基礎,去預測 PM2.5 的汙染: https://pdfs.semanticscholar.org/9a9e/838619bd4fecb882aade0c96a84dc59a2c6d.pdf
小結
這一集我們介紹了最老式的一種結構 ─ ANN,很明顯地這結構最大特性就是:只會學習已經固定的規則,其實所謂學習,就是給 ANN 已經知道的輸入跟輸出,讓 ANN 去湊答案而已。
但也因為這樣的特性,我們發現其實只要動點小手腳, ANN 就可以變成預測結果是有用或無用的計算系統。之後的章節,我們將逐次把多種 NN 抓出來,一一舉例讓大家試試,歡迎大家一起,自己寫些小的程式來驗證。
(責任編輯:楊子嫻)
- 【開箱評測】用Mbed上手開發DSI 2599開發板 - 2020/08/03
- 【OpenVINO™教學】自製麵包影像辨識POS機的應用 - 2019/12/24
- 【邊緣運算】OpenVINO好夥伴 — athena A1 Kit x86單板 - 2019/11/18
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!