神经网络如何工作

预测器与分类器

预测器:接受输入,做出应有的预测,输出结果。根据结果与已知真实示例比较,得到误差,调整内部参数。

分类器:可以对输入进行分类。

​ 简单线性分类器的限制:如果不能用一条直线把根本性的问题划分开来,就没用

神经元

观察表明,神经元不会立即反应,而是会抑制输入,直到输入增强,强到可以触发输出(阈值)。

一般使用S函数(Signoid Function):y = 1 / (1 + e^-x)

单个神经元逻辑示例

神经网络

神经网络示例

第一层是输入层,只是输入而已,不做其它事,无需任何计算。

第二层是隐藏层

第三层是输出层

计算下一层的公式:X = W * I(W是权重矩阵),然后应用S函数

调整权重

反向传播:将误差通过神经网络,从输出反向传播到网络中。

梯度下降:通过偏层数,找到梯度为负的方向并前进。

梯度公式

权重变化式:△Wj,k = α · Ek · Ok(1 - Ok) · Oj^T

准备数据

由于S函数的值在0-1间且不可能等于0或1,所以输入值和目标值都必须在0-1间。

随机初始权重

数学家所得到的经验规则是,我们可以在一个节点传入链接数量平方根倒数的大致范围内随机采样,初始化权重。

禁止将初始权重设定为0,也禁止将其设定为相同的值。

使用Python进行DIY

神经网络类

NeuralNetwork.py文件,神经网络类

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
import numpy
import scipy.special # sigmoid函数


# 神经网络类
class NeuralNetwork:
def __init__(self, input_num, hidden_num, output_num, learning_rate):
self.i_num = input_num
self.h_num = hidden_num
self.o_num = output_num
self.lr = learning_rate
# 权重矩阵
# 简单线性
# self.wih = numpy.random.rand(self.h_num, self.i_num) - 0.5
# self.who = numpy.random.rand(self.o_num, self.h_num) - 0.5
# 正态分布
self.wih = numpy.random.normal(0.0, pow(self.h_num, -0.5), (self.h_num, self.i_num))
self.who = numpy.random.normal(0.0, pow(self.o_num, -0.5), (self.o_num, self.h_num))

# 激活函数
self.activation_func = lambda x: scipy.special.expit(x)
self.inverse_activation_func = lambda x: scipy.special.logit(x)

pass

# 训练
def train(self, inputs_list, targets_list):
# 转换成数组
inputs = numpy.array(inputs_list, ndmin=2).T
targets = numpy.array(targets_list, ndmin=2).T

# 隐藏层
hidden_outputs = self.activation_func(numpy.dot(self.wih, inputs))

# 输出层
final_outputs = self.activation_func(numpy.dot(self.who, hidden_outputs))

# 误差
output_errors = targets - final_outputs
hidden_errors = numpy.dot(self.who.T, output_errors)

# 更新权重
self.who += self.lr * numpy.dot((output_errors * final_outputs * (1 - final_outputs)), numpy.transpose(hidden_outputs))
self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)), numpy.transpose(inputs))
pass

# 查询
def query(self, inputs):
# 输入层->隐藏层
hidden_inputs = numpy.dot(self.wih, inputs)
hidden_outputs = self.activation_func(hidden_inputs)
# 隐藏层->输出层
final_inputs = numpy.dot(self.who, hidden_outputs)
final_outputs = self.activation_func(final_inputs)
return final_outputs
pass

包括初始化、训练、和查询

训练

使用Mnist手写数字数据集:http://yann.lecun.com/exdb/mnist/

CSV文件:https://pjreddie.com/projects/mnist-in-csv/

训练、测试代码:

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
import numpy
import NeuralNetwork


training_file_loc = r'./MnistDataSets/mnist_train.csv'
testing_file_loc = r'./MnistDataSets/mnist_test.csv'
input_node_num = 784
hidden_node_num = 150
output_node_num = 10
learning_rate = 0.1
epochs = 2

n = NeuralNetwork.NeuralNetwork(input_node_num, hidden_node_num, output_node_num, learning_rate)

# 加载训练集csv文件
training_data_file = open(training_file_loc, 'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
# print(len(training_data_list))

# 训练
for e in range(epochs):
for training_data in training_data_list:
all_values = training_data.split(',')
inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
targets = numpy.zeros(output_node_num) + 0.01
targets[int(all_values[0])] = 0.99
n.train(inputs, targets)
pass
pass

# 测试
test_data_file = open(testing_file_loc, 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()

score = 0
for test_data in test_data_list:
all_values = test_data.split(',')
correct_num = int(all_values[0])
outputs = n.query((numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01)
output_num = numpy.argmax(outputs)
if output_num == correct_num:
score += 1
else:
print("输出错误:将", correct_num, "识别为", output_num)
pass
pass
print("分数:", score)

其它

向后查询

输入数字,生成图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 反向查询
def back_query(self, targets):
final_outputs = numpy.array(targets, ndmin=2).T
final_inputs = self.inverse_activation_func(final_outputs)

hidden_outputs = numpy.dot(self.who.T, final_inputs)
hidden_outputs -= numpy.min(hidden_outputs)
hidden_outputs /= numpy.max(hidden_outputs)
hidden_outputs = hidden_outputs * 0.98 + 0.01
hidden_inputs = self.inverse_activation_func(hidden_outputs)

inputs = numpy.dot(self.wih.T, hidden_inputs)
inputs -= numpy.min(inputs)
inputs /= numpy.max(inputs)
inputs = inputs * 0.98 + 0.01

return inputs

生成的数字0

旋转图像

可以用Rotate函数将每个图像旋转正负10度,增强训练效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for e in range(epochs):
for training_data in training_data_list:
all_values = training_data.split(',')
inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
targets = numpy.zeros(output_node_num) + 0.01
targets[int(all_values[0])] = 0.99
n.train(inputs, targets)
# 旋转
input_rotate_image_1 = scipy.ndimage.rotate(inputs.reshape(28, 28), 10, cval=0.01, reshape=False)
input_rotate_image_2 = scipy.ndimage.rotate(inputs.reshape(28, 28), -10, cval=0.01, reshape=False)
n.train(input_rotate_image_1.reshape(784), targets)
n.train(input_rotate_image_2.reshape(784), targets)
pass
pass