0%

深度学习初探

Talk is cheap, show me the code.

开始接触深度学习。

虽然 TensorFlow 是很早就开始折腾了,不过做的基本是研究它的底层实现方面的工作,真正上层应用方面的原理了解的很少。但是之后的工作需要用到这方面的知识了,因此学习一下。

看了不少教程,觉得最好的方法还是跟着个 C 的裸程序走一遍会印象深刻些~


Basic Knowledge

深度学习只是机器学习这个大类中的一种,这种算法其实很早就有了,只是受限于以前机器的计算能力跟不上,因而由于近几年计算力的飞速提升而重新受到关注。

神经网络的算法最早起源于感知机,深度神经网络即隐层层次比较多的神经网络,其实本质就是多层感知机

然后整个神经网络的计算其实就有点像是在用大量数据加上大计算量去暴力拟合一个多维函数的过程(在多维函数空间中搜索最小值?)。

基本过程是数据进来,沿着各层神经网络算一遍,走到输出层输出结果,这个结果跟实际值肯定存在误差,然后把误差反向沿着神经网络传播一遍,逐层用梯度下降法修正神经网络中的各个参数,这样就是一次样本训练了。

网络越深,参数成倍增长,计算量也是成倍增长。

用大量样本训练出参数之后,就可以用这些练好的参数来进行预测了。

Show me the Code

下面是一个 C++ 写的 1 层隐层的全连接网络,数据来源于这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/* ***********************************************
MYID : Chen Fan
LANG : G++ --std=c++11
PROG : BP Neural Network
************************************************ */

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <functional>
#include <random>

#define INP 4
#define HID 10
#define OUT 3
#define LR 0.1
// Learning Rate

/*
Iris DataBase
4 input neurons
HID hidden neurons
3 output neurons
*/

using namespace std;

default_random_engine engine;
uniform_real_distribution<float> distr(0, 1);
function<float()> rnd = bind(distr, engine);

float train_data[3][25][4];
float test_data[3][25][4];

class nodeclass
{
public:
int num_input;
vector<float> weight_input;
float value;
function<float(float)> f;

nodeclass(int n, function<float(float)> ff): num_input(n), f(ff)
{
init_weight();
}
~nodeclass(){}
void init_weight()
{
for (int i=0;i<num_input;i++)
weight_input.push_back(rnd());
}
void calcf(float x)
{
value = f(x);
}
};

vector<nodeclass*> inpnode, hidnode, outnode;

float sigmoid(float x)
{
if (x < -45.0) //too big and too small
return 0.0;
else if (x > 45.0)
return 1.0;
else
return 1.0 / (1.0 + exp(-x));
}

void init_nodes()
{
for (int i=0;i<INP;i++)
inpnode.push_back(new nodeclass(0, NULL));
for (int i=0;i<HID;i++)
hidnode.push_back(new nodeclass(INP, sigmoid));
for (int i=0;i<OUT;i++)
outnode.push_back(new nodeclass(HID, sigmoid));
}

void init_dataset()
{
FILE * fp = fopen("iris.data", "r");
for (int j=0; j<3; j++)
{
char temp[100];
for (int i=0; i<25; i++)
fscanf(fp, "%f,%f,%f,%f,%s", &train_data[j][i][0], &train_data[j][i][1], &train_data[j][i][2], &train_data[j][i][3], temp);
for (int i=0; i<25; i++)
fscanf(fp, "%f,%f,%f,%f,%s", &test_data[j][i][0], &test_data[j][i][1], &test_data[j][i][2], &test_data[j][i][3], temp);
}
fclose(fp);
}

void inference(int type, int clas, int index)
{
// prepare the input data
if (type) //type 0 means train data, 1 means test data
{
int i = 0;
for (auto now : inpnode)
now->value = test_data[clas][index][i++];
} else
{
int i = 0;
for (auto now : inpnode)
now->value = train_data[clas][index][i++];
}
// calculate the hidden layer
for (auto now : hidnode)
{
float temp = 0.0;
int i = 0;
for (auto front : inpnode)
temp += front->value * now->weight_input[i++];
now->calcf(temp);
}
// calculate the output layer
for (auto now : outnode)
{
float temp = 0.0;
int i = 0;
for (auto front : hidnode)
temp += front->value * now->weight_input[i++];
now->calcf(temp);
}
}

void train()
{
for (int j=0;j<3;j++)
{
float expected[] = {0.0, 0.0, 0.0};
expected[j] = 1.0;
for (int index = 0; index < 25; ++index)
{
inference(0, j, index);
//train output -> hidden
int i = 0;
for (auto now : outnode)
{
float delta = (expected[i++] - now->value) * (1 - now->value) * now->value;
int k = 0;
for (auto front : hidnode)
now->weight_input[k++] += LR * delta * front->value;
}
//train hidden -> input
i = 0;
for (auto now : hidnode)
{
float delta = 0.0;
int k = 0;
for (auto back : outnode)
delta += (expected[k++] - back->value) * (1 - back->value) * back->value * back->weight_input[i] * (1 - now->value) * now->value;
k = 0;
for (auto front : inpnode)
now->weight_input[k++] += LR * delta * front->value;
i++;
}
}
}
}

int select(vector<nodeclass*> arr)
{
int max = (arr[1]->value > arr[0]->value) ? 1 : 0;
if (arr[2]->value > arr[max]->value)
max = 2;
return max;
}

int test()
{
int count = 0;
for (int clas = 0; clas < 3; ++clas)
{
for (int index = 0; index < 25; ++index)
{
inference(0, clas, index);
if (select(outnode) == clas)
{
++count;
}
}
}
printf("Accuracy rate: %lf\n", (double)count / 75);
return count;
}

int main()
{
//init the nodes & the weights of NN
init_nodes();
//init the train data and test data
init_dataset();
//start training
while (test() < 75)
{
train();
}
//test
test();

return 0;
}

Notes

上面这个神经网络非常简单,其他还有诸如 CNN(卷积神经网络)、RNN(循环神经网络)、FCN(全卷积网络)等等等等各种算法。

作为一个数学本来就不太好的人,其中深层的数学原理我也不想再深究了(毕竟也不一定能懂),其实光是反向推回来的求导这里就有点爆炸了,高数忘了很多,更不用说之后还可能有多维矩阵的求导。

幸而这部分到时候都有 TensorFlow 这样的框架直接搞定了,更多的可能还是要动更底层的内容。