因為練習 c 的關係, 看到有變態用 c 來手寫 Deep Learning, 就玩看看筆記下, 結果連程式碼都沒附真是狠, 希望不要太早陣亡 XD
Single Input Single Output Neural Network
他第一個例子用天氣來預測 開心
或 難過
, 非常簡單就是 溫度 * 權重 = 預測值
輸入 -> 權重 -> 輸出
input data -> weight -> output (predicted value)
1 | #include <stdio.h> |
c# 如下, 老生常談這裡一樣要先開 <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
這樣寫起來比較有趣
1 | double[] temperature = {12, 23, 50, -10, 16}; |
Single Input Multiple Output Neural Network
假如我們知道這個人心情不太美麗, 我們能否計算 溫度 濕度 空氣品質?
Sad * 溫度權重
0.9 * -20
Sad * 濕度權重
0.9 * 95
Sad * 空氣品質權重
0.9 * 201
他程式碼裡的 input_scalar 即 SadScalar 純量
只有大小沒有方向的,如數量、距離、速度或溫度
1 | #include <stdio.h> |
c#
1 | unsafe |
Multiple Input Single Output
(temperature * weight1) + (humidity * weight2) + (air qualtity * weight3) = Sad (預測值)
1 | #include <stdio.h> |
c#
1 | unsafe |
Multiple input Multiple output
這邊算法跟 Multiple Input Single Output 大同小異
只不過 output 變成三個, 這裡每一種 output 的 權重都不相同
1 | temperature humidity air quality |
1 | #include <stdio.h> |
c#
1 | unsafe |
hidden layer
由 Multiple input Multiple output 延伸, 只不過中間卡一層隱藏層, 先算出隱藏層數值後, 然後再去做一次加權運算最後得到輸出值
1 | temperature humidity air quality |
1 | hidden[0] hidden[1] hidden[2] |
1 | #include <stdio.h> |
c#
1 | unsafe |
finding error
predicted_value = input * weight
error = power(predicted_value - expected_value)
weight = 0.8
expected_value = 26
input = 25
predicted_value = 25 * 0.8
error = power( 20 - 26 )
error = 36
實作新增兩隻函數
double find_error(double input, double weight, double expected_value)
double find_error_simple(double yhat, double y)
1 | #include <stdio.h> |
c#
1 | unsafe |
暴力學習
這感覺有點像是猜數字遊戲, 反正沒猜中就調整數值
1 | #include <stdio.h> |
這裡在 c# 比較難搞, 因為 c# 用 double 會造成精度損失, 所以型別要改用 decimal 提高精度
這裡可以先看下實驗, 他會輸出 0.1999999999998181
1 | double a = 5000.2, b = 5000; |
另外 c# 的 Math.Pow 不支援 decimal, 所以只能自己算
或是這裡沿用 double 然後用 Math.Round 取個小數 8 ~ 10 位
1 | decimal weight = 0.5m; |
Normalizing DataSets
他這裡比較簡單, 就把每個 array 裡面的最大值撈出來, 接著每個 array 的 element 除最大值
1 | #include <stdio.h> |
c# 如下
1 | unsafe |
Random Initialzation of Weights
Synapse 突觸
神經元之間,或神經元與肌細胞、腺體之間通信的特異性接頭
他這裡用兩個特徵 hours of work out
hours of rest data
當作範例, 中間有三個隱藏的 node
最後有一個輸出
然後跑亂數, 最後得到權重 (Synapse)
1 | synapse 0 weight: |
c 程式碼
1 | #include <stdio.h> |
c# 如下
這裡唯一不同應該是撈亂數的方法, c 的 rand() 會撈 0 ~ 32767, 在 c# 可以用 Next(0,32767) 來指定
1 | unsafe |
Forward Propagation
1 | #include <stdio.h> |
這裡先新增 normalize_data_2d
函數, 先取 matrix 內的最大值, 接著 loop 每個數值 / max 即可
1 | void normalize_data_2d(int ROW, int COL, double input_matrix[ROW][COL], double output_matrix[ROW][COL]) |
然後宣告 2(高 NUM_OF_FEATURES) * 3(寬 NUM_OF_EXAMPLES)
的 raw_x 變數, 接著讓他正規化
1 | double raw_x[NUM_OF_FEATURES][NUM_OF_EXAMPLES] = { |
接著宣告 1(高) * 3(寬 NUM_OF_EXAMPLES)
的 raw_y 變數, 呼叫正規化
1 | double raw_y[1][NUM_OF_EXAMPLES] = { |
接續定義函數 weight_random_initialization
他會隨機取得 0 ~ 9 之間的數字, 最後 /10 會得到 0.x 塞入 2d array 內
最後就可以獲得亂數 0.0 ~ 0.9 的權重weight_random_initialzation_1d
也是類似效果, 只不過換種寫法適用 1d array
1 | void weight_random_initialization(int HIDDEN_LEN, int INPUT_LEN, double weights_matrix[HIDDEN_LEN][INPUT_LEN]) |
接著呼叫之前寫過的函數 multiple_input_multiple_output_nn
train_x_eg1 => input
syn0 => 輸入層到隱藏層的權重矩陣
計算隱藏層的加權總和 z1_eg1
1 | multiple_input_multiple_output_nn(train_x_eg1, NUM_OF_FEATURES, z1_eg1, NUM_OF_HID_NODES, syn0); |
然後定義激活函數 sigmoid
1 | double sigmoid(double x) |
以激活函數算出隱藏層的輸出 a1_eg1
1 | vector_sigmoid(z1_eg1, a1_eg1, NUM_OF_HID_NODES); |
以 multiple_input_single_output
算出輸出層的加權總和 z2_eg1
1 | z2_eg1 = multiple_input_single_output(a1_eg1, syn1, NUM_OF_HID_NODES); |
以激活函數算出最終值
1 | yhat_eg1 = sigmoid(z2_eg1); |
他的 c# 如下
1 | unsafe |