本文均屬自己閱讀源代碼的點滴總結。轉賬請注明出處謝謝。
歡迎和大家交流。qq:1037701636 email:gzzaigcn2009@163.com
寫在前面的閑話:
自我感覺自己應該不是一個非常擅長學習算法的人。過去的一個月時間里由於須要去接觸了BP神經網絡。在此之前一直都覺得算法界的神經網絡、蟻群算法、魯棒控制什么的都是特別高大上的東西,自己也就聽聽好了,未曾去觸碰與了解過。這次和BP神經網絡的邂逅。讓我初步掌握到。理解透徹算法的基本原理與公式,轉為計算機所能識別的代碼流,這應該就是所謂的數學和計算機的完美結合吧,難道這過程也就是所謂的ACM嗎?
1.BP神經網絡的基本概念和原理
看了網絡上非常多關於神經網絡相關的資料,了解到BP神經網絡是最為簡單和通用的,剛好所需完畢的工作也主要是簡單的字符識別過程。
BP神經網絡的概念:
BP網絡能學習和存貯大量的輸入-輸出模式映射關系。而無需事前揭示描寫敘述這樣的映射關系的數學方程。
BP神經網絡的基本模型圖例如以下所看到的:

我們所要做的就是依據自身的需求建立一個屬於我們自己的BP神經網絡。
當訓練達到規定誤差或一定訓練次數。則結束訓練。




顯然不管是在正向梯度還是負向梯度,在離散情況下都須要不斷的將權值往誤差極小值的地方調整。而調整的速率值eta(也稱權值的步進值)關乎着整個神經網絡的訓練速度。






3.BP神經網絡算法用C語言的實現
3.1.初始的准備工作,建立三個層的數據結構體:分別表示樣本信息、輸入層的信息、隱藏層的信息以及輸出層的信息,終於歸於到一個BP核心算法的結構體bp_alg_core_params;
typedef struct { unsigned int img_sample_num; //待訓練的圖像字符、數字樣本個數 unsigned int img_width; unsigned int img_height; unsigned char *img_buffer; }img_smaple_params; typedef img_smaple_params* hd_sample_params; typedef struct { unsigned int in_num; //輸入層節點數目 double *in_buf; //輸入層輸出數據緩存 double **weight; //權重,當前輸入層一個節點相應到多個隱層 unsigned int weight_size; double **pri_deltas; //記錄先前權重的變化值。用於附加動量 double *deltas; //當前計算的隱層反饋回來的權重矯正 }bp_input_layer_params; typedef bp_input_layer_params* hd_input_layer_params; typedef struct { unsigned int hid_num; //隱層節點數目 double *hid_buf; //隱層輸出數據緩存 double **weight; //權重。當前隱層一個節點相應到多個輸出層 unsigned int weight_size; double **pri_deltas; //記錄先前權重的變化值,用於附加動量 double *deltas; //當前計算的輸出反饋回來的權重矯正值 }bp_hidden_layer_params; typedef bp_hidden_layer_params* hd_hidden_layer_params; typedef struct { unsigned int out_num;//輸出層節點數目 double *out_buf; //輸出層輸出數據緩存 double *out_target; }bp_out_layer_params; typedef bp_out_layer_params* hd_out_layer_params; typedef struct { unsigned int size; //結構體大小 unsigned int train_ite_num; //訓練迭代次數 unsigned int sample_num; //待訓練的樣本個數 double momentum; //BP閾值調整動量 double eta; //訓練步進值。學習效率 double err2_thresh; //最小均方誤差 hd_sample_params p_sample; //樣本集合參數 hd_input_layer_params p_inlayer; //輸入層參數 hd_hidden_layer_params p_hidlayer;//隱藏層參數 hd_out_layer_params p_outlayer;//輸出層參數 }bp_alg_core_params;
3.2.參數的初始化。主要包含對計算緩存區的分配。
如果這里分別有M,N。K表示輸入、隱藏、輸出的節點數目。
那么一個輸入緩存區大小:分配大小M+1個。同理隱藏層和輸出分別分配:N+1,P+1個;數據量大小默認雙精度的double類型。
權值的緩沖區大小:一個依據BP網絡的算法,一個節點到下一層的節點分別須要具備一一相應。故這是一個二維數組的形式存在。我們分配輸入層權值空間大小為(M+1)(N+1)的大小。隱藏權值空間大小為(N+1)(P+1);
當然對於權值的矯正量。其是一個依據節點的輸出值向后反饋的一個變量,實際就是多對1的反饋。而通過公式能夠看到我們能夠僅僅採用一維數組來表示每個節點的反饋矯正值(不基於輸入節點的數據。即例如以下的變量:
同理終於隱藏層到輸出層的反饋矯正、輸出層和隱藏層的反饋矯正都以一個一位變量的形式存在,僅僅是在計算權值時要結合節點的輸入數據來進行2維矯正。
完畢二維數組的動態分配過程函數例如以下所看到的:
double** alloc_2d_double_buf(unsigned int m, unsigned int n) { unsigned int i; double **buf = NULL; double *head; /*分配一個數組指針空間+ 2維數據緩存空間*/ buf = (double **)malloc(sizeof(double *)*m + m*n*sizeof(double)); if(buf == NULL) { ERR("malloc error!"); exit(1); } head = (double *)(buf + m); memset((void *)head, 0x00, sizeof(double)*m*n);//clear 2d buf for(i = 0; i < m; i++) { buf[i] = head + i*n; DEG("alloc_2d_double_buf, addr = 0x%x", buf[i] ); } return buf; }
3.3 BP神經網絡訓練過程和不斷的權值矯正
依次經過forward向前刺激。權值矯正值計算,權值調整,樣本均分誤差計算。
以一次樣本數全部樣本節點計算完后做均方誤差,誤差滿足一定的閾值就說明BP神經網絡訓練能夠基本結束(一般定義可接受的誤差在0.001左右):
int bp_train(bp_alg_core_params *core_params) { unsigned int i, j, k; unsigned int train_num, sample_num; double err2;//均分誤差 DEG("Enter bp_train Function"); if(core_params == NULL) { ERR("Null point Entry"); return -1; } train_num = core_params->train_ite_num;//迭代訓練次數 sample_num = core_params->sample_num;//樣本數 hd_sample_params p_sample = core_params->p_sample; //樣本集合參數 hd_input_layer_params p_inlayer = core_params->p_inlayer; //輸入層參數 hd_hidden_layer_params p_hidlayer = core_params->p_hidlayer; //隱藏層參數 hd_out_layer_params p_outlayer = core_params->p_outlayer; //輸出層參數 DEG("The max train_num = %d", train_num); /*依次依照訓練樣本數目進行迭代訓練*/ for(i = 0; i < train_num; i++) { err2 = 0.0; DEG("current train_num = %d", i); for(j = 0 ; j < sample_num; j++) { DEG("current sample id = %d", j); memcpy((unsigned char*)(p_inlayer->in_buf+1), (unsigned char*)sample[j], p_inlayer->in_num*sizeof(double)); memcpy((unsigned char*)(p_outlayer->out_target+1), (unsigned char*)out_target[j%10], p_outlayer->out_num*sizeof(double)); /*輸入層到隱藏層的向前傳遞輸出*/ bp_layerforward(p_inlayer->in_buf, p_hidlayer->hid_buf, p_inlayer->in_num, p_hidlayer->hid_num, p_inlayer->weight); /*隱藏層到輸出層的向前傳遞輸出*/ bp_layerforward(p_hidlayer->hid_buf, p_outlayer->out_buf, p_hidlayer->hid_num, p_outlayer->out_num, p_hidlayer->weight); /*輸出層向前反饋錯誤到隱藏層,即權值矯正值*/ bp_outlayer_deltas(p_outlayer->out_buf, p_outlayer->out_target, p_outlayer->out_num, p_hidlayer->deltas); /*隱藏層向前反饋錯誤到輸入層,權值矯正值依賴於上一層的調整值*/ bp_hidlayer_deltas(p_hidlayer->hid_buf, p_hidlayer->hid_num, p_outlayer->out_num, p_hidlayer->weight, p_hidlayer->deltas, p_inlayer->deltas); /*調整隱藏層到輸出層的權值*/ adjust_layer_weight(p_hidlayer->hid_buf, p_hidlayer->weight, p_hidlayer->pri_deltas, p_hidlayer->deltas, p_hidlayer->hid_num, p_outlayer->out_num, core_params->eta, core_params->momentum); /*調整隱藏層到輸出層的權值*/ adjust_layer_weight(p_inlayer->in_buf, p_inlayer->weight, p_inlayer->pri_deltas, p_inlayer->deltas, p_inlayer->in_num, p_hidlayer->hid_num, core_params->eta, core_params->momentum); err2 += calculate_err2(p_outlayer->out_buf, p_outlayer->out_target, p_outlayer->out_num);//統計全部樣本遍歷一次后的均分誤差 } /*一次樣本處理后的均分誤差統計*/ err2 = err2/(double)(p_outlayer->out_num*sample_num); INFO("err2 =%08f\n",err2 ); if(err2 < core_params->err2_thresh) { INFO("BP Train Success by costs vaild iter nums: %d\n", i); return 1; } } INFO("BP Train %d Num Failured! need to modfiy core params\n", i); return 0; }
4.總結
BP神經網絡在理解完算法的核心思想后,用代碼的形式去實現往往會變得事半功倍,而如果一股腦兒直接拿到code去分析的做法不推薦。由於不了解核心思想的基礎下,無法對算法的參數進行部分的改動以及優化,盲目改動往往會造成實驗的失敗。
本算法應該滿足了BP神經網絡的基本訓練過程。至於不管是識別,預測還是其它,都能夠在獲取理想樣本源和目標源的基礎上對BP神經網絡進行訓練與學習,使得其具備了一定的通用性。后期要做的是將上述浮點的處理過程竟可能的轉為定點化的DSP來處理,將其應用到嵌入式設備中去。
注:
由於非常多人向我咨詢code,所以上傳了基於BP神經網絡的簡單字符識別算法自小結(C語言版) 。本人已不再研究,謝謝。