神經(jīng)網(wǎng)絡(luò)入門(mén)

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ē)輛行駛的方向。


ALVINN

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ò)輸出值的向量)
  1. 把實(shí)例$x$輸入網(wǎng)絡(luò),并計(jì)算網(wǎng)絡(luò)中每個(gè)單元$u$的輸出$o$
  2. 對(duì)于網(wǎng)絡(luò)的每一個(gè)輸出單元$k$役耕,計(jì)算它的誤差項(xiàng)$\delta_k$ $$\delta_k = o_k(1-o_k)(t_k-o_k)$$
  3. 對(duì)于網(wǎng)絡(luò)的每個(gè)隱藏單元$h$采转,計(jì)算它的誤差$\delta_h$ $$\delta_h = o_h(1-o_h)\sum_{k\in outputs}w_{kh}\delta_k$$
  4. 更新每個(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á)式较木。

  1. 輸出單元的權(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}$$

  1. 隱藏單元的權(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 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耍目,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子徐绑,更是在濱河造成了極大的恐慌邪驮,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傲茄,死亡現(xiàn)場(chǎng)離奇詭異毅访,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)盘榨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)喻粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人草巡,你說(shuō)我怎么就攤上這事守呜。” “怎么了山憨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵查乒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我郁竟,道長(zhǎng)玛迄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任棚亩,我火速辦了婚禮蓖议,結(jié)果婚禮上虏杰,老公的妹妹穿的比我還像新娘。我一直安慰自己勒虾,他們只是感情好纺阔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著从撼,像睡著了一般州弟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上低零,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天婆翔,我揣著相機(jī)與錄音,去河邊找鬼掏婶。 笑死啃奴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雄妥。 我是一名探鬼主播最蕾,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼老厌!你這毒婦竟也來(lái)了瘟则?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤枝秤,失蹤者是張志新(化名)和其女友劉穎醋拧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體淀弹,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丹壕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了薇溃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菌赖。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沐序,靈堂內(nèi)的尸體忽然破棺而出琉用,到底是詐尸還是另有隱情,我是刑警寧澤策幼,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布邑时,位于F島的核電站,受9級(jí)特大地震影響垄惧,放射性物質(zhì)發(fā)生泄漏刁愿。R本人自食惡果不足惜绰寞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一到逊、第九天 我趴在偏房一處隱蔽的房頂上張望铣口。 院中可真熱鬧,春花似錦觉壶、人聲如沸脑题。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叔遂。三九已至,卻和暖如春争剿,著一層夾襖步出監(jiān)牢的瞬間已艰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蚕苇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哩掺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓涩笤,卻偏偏與公主長(zhǎng)得像嚼吞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹬碧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容