1 人工神經(jīng)網(wǎng)絡(luò)簡(jiǎn)介
生物學(xué)動(dòng)機(jī)
人工神經(jīng)網(wǎng)絡(luò)ANN的研究一定程度上受到了生物學(xué)的啟發(fā),生物的學(xué)習(xí)系統(tǒng)由相互連接的神經(jīng)元(neuron)組成的異常復(fù)雜的網(wǎng)格忌愚。而人工神經(jīng)網(wǎng)絡(luò)由一系列簡(jiǎn)單的單元相互密集連接構(gòu)成的夹纫,其中每一個(gè)單元有一定數(shù)量的實(shí)值輸入寸宵,并產(chǎn)生單一的實(shí)數(shù)值輸出恤煞。
據(jù)估計(jì)人類(lèi)的大腦是由大約$10{11}$次方個(gè)神經(jīng)元相互連接組成的密集網(wǎng)絡(luò)姓赤,平均每個(gè)神經(jīng)元與其他$104$個(gè)神經(jīng)元相連。神經(jīng)元的活性通常被通向其他神經(jīng)元的連接激活或抑制既们。
2 神經(jīng)網(wǎng)絡(luò)表示
1993年的ALVINN系統(tǒng)是ANN學(xué)習(xí)的一個(gè)典型實(shí)例濒析,這個(gè)系統(tǒng)使用一個(gè)學(xué)習(xí)到的ANN以正常的速度在高速公路上駕駛汽車(chē)。ANN的輸入是一個(gè)30*32像素的網(wǎng)格啥纸,像素的亮度來(lái)自一個(gè)安裝在車(chē)輛上的前向攝像機(jī)号杏。ANN的輸出是車(chē)輛行駛的方向。
3 適合神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)的問(wèn)題
- 實(shí)例是用很多屬性-值表示的
- 目標(biāo)函數(shù)的輸出可能是離散值斯棒、實(shí)數(shù)值或者由若干實(shí)數(shù)屬性或者離散屬性組成的向量
- 訓(xùn)練數(shù)據(jù)可能包含錯(cuò)誤
- 可容忍長(zhǎng)時(shí)間的訓(xùn)練
- 可能需要快速求出目標(biāo)函數(shù)值
- 人類(lèi)能否理解學(xué)到的目標(biāo)函數(shù)不是重要的盾致。
4 感知器
感知器以一個(gè)實(shí)數(shù)值向量作為輸入,計(jì)算這些輸入的線(xiàn)性組合 名船,然后如果結(jié)果大于某個(gè)閾值绰上,就輸出1,否則輸出-1渠驼。
$$o(x_1, x_2,...,x_n) = \begin{equation}
\begin{cases}
1 \quad if (w_0 + w_1x_1+...+w_nx_n)\
-1 \quad otherwise
\end{cases}
\end{equation} $$
假設(shè)輸入空間(特征空間)是$X \subseteq R^n$蜈块,輸出空間是$Y = {-1, +1}$。輸入$x \subseteq X$是實(shí)例的特征向量迷扇,對(duì)應(yīng)于輸入空間的點(diǎn)百揭;輸出$y \subseteq Y$表示實(shí)例的類(lèi)別。由輸入空間到輸出空間的如下函數(shù)稱(chēng)為感知機(jī):
$$f(x)=sign(w \cdot x + b)$$
其中w b稱(chēng)為感知機(jī)的模型參數(shù)蜓席,w叫做權(quán)值(weight)或者權(quán)值向量(weight vector)器一,b叫做偏置(bias)。$w\cdot x$表示w和x的內(nèi)積厨内。sign是符號(hào)函數(shù):
$$sign(x) = \begin{equation}\begin{cases} +1, x >= 0 \ -1 , x <= 0 \end{cases}\end{equation}$$
感知機(jī)是一種線(xiàn)性分類(lèi)模型祈秕,它的假設(shè)空間是定義在特征空間中的所有線(xiàn)性分類(lèi)模型。
感知機(jī)有如下幾何解釋?zhuān)壕€(xiàn)性方程$$w \cdot x + b = 0$$
對(duì)應(yīng)于特征空間的一個(gè)超平面S雏胃。這個(gè)超平面把特征空間劃分成兩部分请毛,位于兩部分的點(diǎn)分別被分為正負(fù)兩類(lèi)。
5 多層網(wǎng)絡(luò)和反向傳播算法
5.1 可微閾值單元
- 多個(gè)線(xiàn)性單元的連接仍然是產(chǎn)生的是線(xiàn)性函數(shù)瞭亮,而我們更選擇選擇能夠表征非線(xiàn)性函數(shù)的網(wǎng)絡(luò)方仿。
- 感知器單元的不連續(xù)閾值使它不可微,所以不適合梯度下降算法统翩。
我們需要的是這樣一種單元仙蚜,它的輸出是輸入的非線(xiàn)性函數(shù),并且輸出是輸入的可微函數(shù)厂汗。sigmoid單元是一種非常類(lèi)似感知器的單元委粉,并且它基于一個(gè)平滑的可微閾值函數(shù)。
$$o = \sigma(w * x)$$
$$\sigma(y)=\frac1{1+e^{-y}}$$
- $\sigma$經(jīng)常被稱(chēng)為Sigmoid函數(shù)或者logistic函數(shù)娶桦。它的輸出范圍是0到1艳丛,隨輸入單調(diào)遞增匣掸。因?yàn)檫@個(gè)函數(shù)把非常大的輸入值映射到一個(gè)小范圍的輸出,它經(jīng)常被稱(chēng)為Sigmoid單元的擠壓函數(shù)氮双。
- sigmoid函數(shù)的導(dǎo)數(shù)很容易用它的輸出表示碰酝。
$$\frac{\mathrmgz1ermo \sigma(y)}{\mathrmmhzys8f y} = \sigma(y) * (1 - \sigma(y))$$
5.2 反向傳播算法
對(duì)于一系列確定的單元互聯(lián)形成的多層網(wǎng)絡(luò),反向傳播算法可用來(lái)學(xué)習(xí)這個(gè)網(wǎng)絡(luò)的權(quán)值戴差。它采用梯度下降方法試圖最小化網(wǎng)絡(luò)輸出值與目標(biāo)值之間的誤差平方送爸。
$$E(w) = \frac1{2}\sum_{d\in{D}}\sum_{k\in{outputs}}(t_{kd}-o_{kd})^2$$
其中outputs是網(wǎng)絡(luò)輸出單元的集合,$t_{kd}$和$o_{kd}$是與訓(xùn)練樣例$d$和第$k$個(gè)輸出單元相關(guān)的輸出值暖释。
反向傳播算法面臨的學(xué)習(xí)問(wèn)題是搜索一個(gè)巨大的假設(shè)空間袭厂,這個(gè)空間由網(wǎng)絡(luò)中所有單元的所有可能的權(quán)值定義。
在多層網(wǎng)絡(luò)中球匕,誤差曲面可能有多個(gè)局部極小值纹磺。梯度下降僅能保證收斂到局部極小值,而未必得到全局最小的誤差亮曹。
包含兩層sigmoid單元的前饋網(wǎng)絡(luò)的反向傳播算法:
- 創(chuàng)建具有$n_{in}$個(gè)輸入橄杨,$n_{hidden}$個(gè)隱藏單元,$n_{out}$個(gè)輸出單元的網(wǎng)絡(luò)照卦。
- 初始化所有的網(wǎng)絡(luò)權(quán)值為小的隨機(jī)值(-0.05-0.05)
- 在遇到終止條件前:
對(duì)于訓(xùn)練樣例train_sample中的每一個(gè)(x, t)(x網(wǎng)絡(luò)輸入值的向量式矫,t網(wǎng)絡(luò)輸出值的向量)
- 把實(shí)例$x$輸入網(wǎng)絡(luò),并計(jì)算網(wǎng)絡(luò)中每個(gè)單元$u$的輸出$o$
- 對(duì)于網(wǎng)絡(luò)的每一個(gè)輸出單元$k$役耕,計(jì)算它的誤差項(xiàng)$\delta_k$ $$\delta_k = o_k(1-o_k)(t_k-o_k)$$
- 對(duì)于網(wǎng)絡(luò)的每個(gè)隱藏單元$h$采转,計(jì)算它的誤差$\delta_h$ $$\delta_h = o_h(1-o_h)\sum_{k\in outputs}w_{kh}\delta_k$$
- 更新每個(gè)網(wǎng)絡(luò)權(quán)值$w_{ji}$ $$w_{ji}=w_{ji} + \Delta w_{ji} $$
其中$\delta_{ji}=\eta \delta_j x_{ji}$
梯度下降更新法則依照以下三者來(lái)更新每一個(gè)權(quán): 學(xué)習(xí)速率$\eta$,該權(quán)值涉及到輸入$x_{ji}$瞬痘,這個(gè)單元輸出的誤差故慈。
為了直觀的理解它,先考慮網(wǎng)絡(luò)的每一個(gè)輸出單元k的$\delta_k$是怎樣計(jì)算的框全。$\delta_k$與delta法則中的$(t_k-o_k)$相似察绷,但乘上了擠壓函數(shù)的導(dǎo)數(shù)$o_k(1-o_k)$。
每個(gè)隱藏單元h的$\delta_h$具有相似的形式竣况。因?yàn)橛?xùn)練樣例僅對(duì)網(wǎng)絡(luò)的輸出提供了目標(biāo)值,所有缺少直接的目標(biāo)值來(lái)計(jì)算隱藏單元的誤差值筒严。因此采取下邊間接辦法計(jì)算隱藏單元的誤差項(xiàng):對(duì)受隱藏單元h影響的每一個(gè)單元的誤差$\delta_h$進(jìn)行加權(quán)求和丹泉,每個(gè)誤差$\delta_k$權(quán)值為$w_{kh}$,$w_{kh}$就是從隱藏單元h到輸出單元k的權(quán)值鸭蛙。這個(gè)權(quán)值刻畫(huà)了隱藏單元h對(duì)于輸出單元k的誤差應(yīng)“負(fù)責(zé)”的程度摹恨。
5.3 反向傳播算法的推導(dǎo)
隨機(jī)的梯度下降算法迭代處理訓(xùn)練樣例,每次處理一個(gè)娶视。對(duì)于每個(gè)訓(xùn)練樣例$d$晒哄,利用這個(gè)樣例的誤差$E_d$的梯度修改權(quán)值睁宰。換句話(huà)說(shuō),對(duì)于每一個(gè)訓(xùn)練樣例$d$寝凌,每個(gè)權(quán)$w_{ji}$增加$\Delta w_{ji}$柒傻。
$$\Delta w_{ji} = -\eta\frac{\partial E_d}{\partial w_{ji}}$$
$$E_d(w) = \frac1{2}\sum_{k\in{outputs}}(t_k - o_k)^2$$
- $x_{ji}$ = 單元$j$的第$i$個(gè)輸入
- $w_{ji}$ = 與單元$j$的第$i$個(gè)輸入相關(guān)聯(lián)的權(quán)值
- $net_j = \sum{w_{ji}x_{ji}}$
- $o_j$ = 單元$j$計(jì)算出的輸出
- $t_j$ = 單元$j$的目標(biāo)輸出
- $\sigma$ = sigmoid函數(shù)
- $outputs =$網(wǎng)絡(luò)最后一層的單元的集合
- $Downstream(j)$單元的直接輸入中包含單元$j$ 的輸出的單元的集合
權(quán)值$w_{ji}$僅能通過(guò)$net_j$影響網(wǎng)絡(luò)的其他部分。所以我們用鏈?zhǔn)揭?guī)則得到 :
$$\frac{\partial E_d}{\partial w_{ji}} = \frac{\partial E_d}{\partial net_j}\frac{\partial net_j}{\partial w_{ji}} = \frac{\partial E_d}{\partial net_j} x_{ji}$$
剩下的任務(wù)就是為$\frac{\partial E_d}{\partial net_j} $導(dǎo)出一個(gè)方便的表達(dá)式较木。
- 輸出單元的權(quán)值訓(xùn)練法則
$$\frac{\partial E_d}{\partial net_j} = \frac{\partial E_d}{\partial o_j} \frac{\partial o_j}{\partial net_j}$$
(1) 考慮公式中的第一項(xiàng)($\frac{\partial E_d}{\partial o_j} $)
$$\frac {\partial E_d}{\partial o_j} = \frac {\partial}{o_j} \frac 1 2 \sum_{k \in outputs} (t_k - o_k)^2$$
除了當(dāng)$k = j $時(shí)红符,所有輸出單元k的導(dǎo)數(shù)$\frac {\partial} {\partial o_j} (t_k-o_k)^2$為0。
所以我們不必對(duì)多個(gè)輸出單元求和伐债,只需設(shè)$k=j$
$$ \begin{split}
\frac{\partial E_d}{\partial o_j} &= \frac{\partial }{\partial o_j}\frac12(t_j-o_j)^2 \
&= (t_j-o_j)\frac{\partial(t_j-o_j)}{\partial o_j} \
&= -(t_j-o_j)\end{split} $$
(2) 考慮公式中的第二項(xiàng)($\frac{\partial o_j}{\partial net_j}$)
既然$o_j=\sigma(net_j)$预侯,導(dǎo)數(shù)$\frac{\partial o_j}{\partial net_j}$就是sigmoid函數(shù)的導(dǎo)數(shù)。
$$\frac {\partial o_j}{\partial net_j} = \frac{\partial\sigma(net_j)}{\partial net_j} = o_j(1-o_j)$$
由上可得:
$$\frac{\partial{E_d}}{\partial{net_j}}=-(t_j-o_j)o_j(1-o_j)$$
輸出單元的隨機(jī)梯度下降法則:
$$\Delta w_{ji}=\eta\frac{\partial E_d}{\partial w_{ji}}=\eta(t_j-o_j)o_j(1-o_j)x_{ji}$$
- 隱藏單元的權(quán)值訓(xùn)練法則
對(duì)于網(wǎng)絡(luò)中的內(nèi)部單元或者隱藏單元的情況峰锁,推導(dǎo)$w_{ji}$必須考慮$w_{ji}$間接地影響網(wǎng)絡(luò)輸出萎馅,從而影響$E_d$。
$net_j$只能通過(guò) $Downstream(j)$中的單元影響網(wǎng)絡(luò)輸出虹蒋,再影響$E_d$糜芳。
$$\begin{split}\frac{\partial E_d}{\partial net_j} &=\sum_{k\in Downstream(j)}\frac{\partial E_d}{\partial net_k}\frac{net_k}{net_j} \&=\sum_{k\in Downstream(j)}-\delta_k\frac{net_k}{net_j}\&=\sum_{k\in Downstream(j)}-\delta_k\frac{\partial net_k}{\partial o_j}\frac {\partial o_j}{\partial net_j}\&=\sum_{k\in Downstream(j)}-\delta_k w_{kj}\frac {\partial o_j}{\partial net_j}\&=\sum_{k\in Downstream(j)}-\delta_k w_{kj}o_j(1-o_j)\end{split}$$
用$\delta_j$表示$-\frac{\partial E_d}{\partial net_j}$,得到:
$$\delta_j=o_j(1-o_j)\sum_k\in Donwstream(j)\delta_k w_{kj}$$
$$\Delta w_{ji}=\eta \delta_j x_{ji}$$
5.4 代碼實(shí)踐
//#include "load_dataset.hpp"
#include <stdint.h>
#include <sys/stat.h>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace cv;
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
uint32_t swap_endian(uint32_t val) {
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
return (val << 16) | (val >> 16);
}
inline cv::Mat str2Mat(const char* pixels, const int row, const int col){
cv::Mat m(row, col, CV_8UC1);
for (int y = 0; y < row; y++) {
memcpy(m.data + y * m.step, pixels + y * col, col);
}
return m;
}
void convert_dataset(const string &image_filename, const string &label_filename, std::vector< std::vector<cv::Mat> > &vvMat) {
vvMat.resize(10);
// Open files
std::ifstream image_file(image_filename, std::ios::in | std::ios::binary);
std::ifstream label_file(label_filename, std::ios::in | std::ios::binary);
//CHECK(image_file) << "Unable to open file " << image_filename;
//CHECK(label_file) << "Unable to open file " << label_filename;
// Read the magic and the meta data
uint32_t magic;
uint32_t num_items;
uint32_t num_labels;
uint32_t rows;
uint32_t cols;
image_file.read(reinterpret_cast<char*>(&magic), 4);
magic = swap_endian(magic);
//CHECK_EQ(magic, 2051) << "Incorrect image file magic.";
label_file.read(reinterpret_cast<char*>(&magic), 4);
magic = swap_endian(magic);
//CHECK_EQ(magic, 2049) << "Incorrect label file magic.";
image_file.read(reinterpret_cast<char*>(&num_items), 4);
num_items = swap_endian(num_items);
label_file.read(reinterpret_cast<char*>(&num_labels), 4);
num_labels = swap_endian(num_labels);
std::cout << "num_items " << num_items << ", num_labels " << num_labels << std::endl;
//CHECK_EQ(num_items, num_labels);
image_file.read(reinterpret_cast<char*>(&rows), 4);
rows = swap_endian(rows);
image_file.read(reinterpret_cast<char*>(&cols), 4);
cols = swap_endian(cols);
std::cout << "rows " << rows << ", cols " << cols << std::endl;
// Storing to db
char label;
char* pixels = new char[rows * cols];
for (int item_id = 0; item_id < num_items; ++item_id) {
image_file.read(pixels, rows * cols);
label_file.read(&label, 1);
cv::Mat img = str2Mat(pixels, rows, cols);
//std::cout << int(label) << std::endl;
//print_mat(img);
//string winname = "0";
//winname[0] += label;
//imshow(winname, img);
//waitKey();
//destroyAllWindows();
vvMat[label].push_back(img);
//break;
}
delete[] pixels;
}
inline double sigmoid(double z) {
return 1.0 / (1.0 + exp(-z));
}
//sigmoid函數(shù)的導(dǎo)數(shù)
inline double sigmoid_prime(double z) {
return sigmoid(z) * (1 - sigmoid(z));
}
const int n_in = 784;
const int n_hidden = 30;
const int n_out = 10;
double w1[n_hidden][n_in];
double w2[n_out][n_hidden];
double b1[n_hidden];
double b2[n_out];
double delta_w1[n_hidden];
double delta_w2[n_out];
double delta_b1[n_hidden];
double delta_b2[n_out];
double eta = 0.01;
void init_wb();
void init_delta();
double evaluate(const vector<vector<Mat>> &vvMatTest, const int num = 1000);
void forward(double* x, double* y, double* h);
void Mat8uc1_darr(const Mat& m, double *arr);
void print_mat(const cv::Mat &m);
void print_array(const double* arr, int size1, int size2 = 0);
void sgd(const vector<vector<Mat>> &vvMatTrain, const int numTrain, const int beginIndex = 0);
int main() {
string data_path = "/Users/zhangxin/datas/mnist/";
string images_test = "t10k-images-idx3-ubyte";
string labels_test = "t10k-labels-idx1-ubyte";
string images_train = "train-images-idx3-ubyte";
string labels_train = "train-labels-idx1-ubyte";
std::vector< std::vector<cv::Mat> > vvMat;
std::vector< std::vector<cv::Mat> > vvMatTest;
convert_dataset(data_path + images_test, data_path + labels_test, vvMatTest);
convert_dataset(data_path + images_train, data_path + labels_train, vvMat);
init_wb();
evaluate(vvMatTest, 2000);
for (int i = 0; i < 1000; i++) {
printf("%4d --------\n", i);
sgd(vvMat, 100, i * 100);
evaluate(vvMatTest, 2000);
}
//evaluate(vvMatTest, 1000);
return 0;
}
void sgd(const vector<vector<Mat>> &vvMatTrain, const int numTrain, const int beginIndex){
init_delta();
for (int j = 0; j < numTrain ; j++) {
for (int i = 0; i < n_out; i++) {
double y[n_out] = {0.0};
double x[n_in] = {0.0};
double h[n_hidden] = {0.0};
Mat8uc1_darr(vvMatTrain[i][(beginIndex + j) % vvMatTrain[i].size()], x);
forward(x, y, h);
double t[n_out] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
t[i] = 1.0;
for (int m = 0; m < n_out; m++) {
delta_w2[m] = delta_b2[m] = y[m] * (1-y[m]) * (t[m] - y[m]);
b2[m] += eta * delta_b2[m];
for (int n = 0; n < n_hidden; n++) {
w2[m][n] += eta * delta_w2[m] * h[n];
}
}
for (int m = 0; m < n_hidden; m++) {
delta_b1[m] = 0.0;
for (int k = 0; k < n_out; k++) {
delta_b1[m] += w2[k][m] * delta_w2[k];
}
delta_w1[m] = delta_b1[m] = delta_b1[m] * h[m] * (1 - h[m]);
b1[m] += eta * delta_b1[m];
for (int n = 0; n < n_in; n++) {
w1[m][n] += eta * delta_w1[m] * x[n];
}
}
}
}
}
double evaluate(const vector<vector<Mat>> &vvMatTest, const int num){
int sum = 0;
int sum_right = 0;
vector<int> vec_sum(10, 0);
vector<int> vec_sum_right(10, 0);
int result[10][10];
memset(result, 0, sizeof(int) * 100);
for (int i = 0; i < n_out && i < vvMatTest.size(); i++) {
for (int j = 0; j < num && j < (int)vvMatTest[i].size(); j++) {//
double x[n_in];
double y[n_out];
double h[n_hidden];
const Mat &img = vvMatTest[i][j];
Mat8uc1_darr(img, x);
// print_mat(img);
// print_array(x, img.rows, img.cols);
// imshow("00", img);
// waitKey();
// continue;
forward(x, y, h);
int predict_label = 0;
double predict_f = y[0];//y.at<double>(0, 0);
//printf("%2d %4d :[ 0] %6.3f, ", i, j, predict_f);
for (int k = 1; k < n_out; k++) {
double f = y[k];//y.at<double>(0, k);
//printf("%d %6.3f, ", k, f);
if (f > predict_f ) {
predict_f = f;
predict_label = k;
}
}
//printf(" predict : %d %6.3f\n", predict_label, predict_f);
vec_sum[i]++;
sum++;
if (predict_label == i) {
sum_right++;
vec_sum_right[i]++;
}
result[i][predict_label]++;
}
}
std::cout << "sum : " << sum << ", sum_right : " << sum_right << std::endl;
for (int i = 0; i < n_out; i++) {
printf(" [%d] [%4d %4d] ", i, vec_sum[i], vec_sum_right[i]);
for (int j = 0; j < n_out; j++) {
printf ("%4d ", result[i][j]);
}
printf("\n");
}
return sum_right / double(sum);
}
void forward(double* x, double* y, double* h){
for (int i = 0; i < n_hidden; i++) {
h[i] = b1[i];
for (int j = 0; j < n_in; j++) {
h[i] += x[j] * w1[i][j];
}
h[i] = sigmoid(h[i]);
}
for (int i = 0; i < n_out; i++) {
y[i] = b2[i];
for (int j = 0; j < n_hidden; j++) {
y[i] += h[j] * w2[i][j];
}
y[i] = sigmoid(y[i]);
}
}
void Mat8uc1_darr(const Mat& m, double *arr) {
for (int y = 0; y < m.rows; y++) {
for (int x = 0; x < m.cols; x++) {
arr[y * m.cols + x] = m.at<uchar>(y,x) / 255.0;
}
}
}
void init_delta(){
memset(delta_w1, 0, sizeof(double) * n_hidden);
memset(delta_w2, 0, sizeof(double) * n_out);
memset(delta_b1, 0, sizeof(double) * n_hidden);
memset(delta_b2, 0, sizeof(double) * n_out);
}
void init_wb() {
std::srand((unsigned)std::time(0)); // use current time as seed for random generator
cout << " init w1 : " << endl;
for (int i = 0; i < n_hidden; i++) {
cout << " w1[" << i << "]" << endl;
for (int j = 0; j < n_in; j++) {
w1[i][j] = std::rand()/double(RAND_MAX) - 0.5;
w1[i][j] *= 0.1;
printf("%6.3f ", w1[i][j]);
}
printf("\n");
}
cout << " init w2 : " << endl;
for (int i = 0; i < n_out; i++) {
cout << " w1[" << i << "]" << endl;
for (int j = 0; j < n_hidden; j++) {
w2[i][j] = std::rand()/double(RAND_MAX) - 0.5;
w2[i][j] *= 0.1;
printf("%6.3f ", w2[i][j]);
}
printf("\n");
}
cout << "init b1" << endl;
for (int i = 0; i < n_hidden; i++) {
b1[i] = std::rand() / double(RAND_MAX) - 0.5;
b1[i] *= 0.1;
printf("%6.3f ", b1[i]);
}
printf("\n");
cout << "init b2" << endl;
for (int i = 0; i < n_out; i++) {
b2[i] = std::rand() / double(RAND_MAX) - 0.5;
b2[i] *= 0.2;
printf("%6.3f ", b2[i]);
}
printf("\n");
}
void print_mat(const cv::Mat &m) {
for (int y = 0; y < m.rows; y++) {
printf("[%4d] ", y);
for (int x = 0; x < m.cols; x++) {
switch(m.type()){
case CV_8UC1:
printf("%3d ", m.data[y * m.step + x]); break;
case CV_64FC1:
printf("%6.3f ", m.at<double>(y,x)); break;
}
}
std::cout << std::endl;
}
}
void print_array(const double* arr, int size1, int size2) {
for (int i = 0; i < size1; i++) {
if( size2 > 1){
printf("[%4d] ", i);
for (int j = 0; j < size2; j++) {
printf("%6.3f ", arr[i * size2 + j]);
}
printf("\n");
}else {
printf("%6.3f ", arr[i]);
}
}
printf("\n");
}
在迭代了1000次后得到 準(zhǔn)確率94.85%千诬, 輸出結(jié)果:
999 --------
sum : 10000, sum_right : 9485
[0] [ 980 965] 965 0 0 1 0 2 6 4 2 0
[1] [1135 1114] 0 1114 3 3 0 1 4 2 8 0
[2] [1032 961] 10 3 961 4 8 2 8 13 21 2
[3] [1010 952] 0 0 15 952 1 15 1 10 14 2
[4] [ 982 929] 1 2 3 2 929 1 10 2 6 26
[5] [ 892 822] 6 1 3 23 3 822 13 4 10 7
[6] [ 958 921] 12 3 4 1 5 8 921 0 4 0
[7] [1028 967] 4 10 17 3 8 1 0 967 3 15
[8] [ 974 918] 5 3 5 14 6 8 5 8 918 2
[9] [1009 936] 10 7 1 14 18 7 1 9 6 936