本文主要讲如何不依赖tenserflow等高级api实现一个简单的神经网络来做分类,所有的代码都在下面;在构造的数据(通过程序构造)上做了验证,经过1个小时的训练分类的准确率可以达到97%。
完整的结构化代码见于:链接地址
先来说说原理
网络构造
上面是一个简单的三层网络;输入层包含节点x1 , x2;隐层包含h1,h2;输出层包含o1。
输入节点的数量要等于输入数据的变量数目。
隐层节点的数量通过经验来确定。
如果只是做分类,输出层一般一个节点就够了。
从输入到输出的过程
1.输入节点的输出等于输入,x1节点输入x1时,输出还是x1.
2. 隐层和输出层的输入i为上层输出的加权求和再加偏置,输出为f(i) , f为激活函数,可以取sigmoid。h1的输出为 sigmoid(w1x1 + w2x2 + b)
误差反向传播的过程
python实现
构造测试数据
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
|
# -*- coding: utf-8 -*- import numpy as np from random import random as rdn ''' 说明:我们构造1000条数据,每条数据有三个属性(用a1 , a2 , a3表示) a1 离散型 取值 1 到 10 , 均匀分布 a2 离散型 取值 1 到 10 , 均匀分布 a3 连续型 取值 1 到 100 , 且符合正态分布 各属性之间独立。 共2个分类(0 , 1),属性值与类别之间的关系如下, 0 : a1 in [1 , 3] and a2 in [4 , 10] and a3 <= 50 1 : a1 in [1 , 3] and a2 in [4 , 10] and a3 > 50 0 : a1 in [1 , 3] and a2 in [1 , 3] and a3 > 30 1 : a1 in [1 , 3] and a2 in [1 , 3] and a3 <= 30 0 : a1 in [4 , 10] and a2 in [4 , 10] and a3 <= 50 1 : a1 in [4 , 10] and a2 in [4 , 10] and a3 > 50 0 : a1 in [4 , 10] and a2 in [1 , 3] and a3 > 30 1 : a1 in [4 , 10] and a2 in [1 , 3] and a3 <= 30 ''' def gendata() : #为a3生成符合正态分布的数据 a3_data = np.random.randn( 1000 ) * 30 + 50 data = [] for i in range ( 1000 ) : #生成a1 a1 = int (rdn() * 10 ) + 1 if a1 > 10 : a1 = 10 #生成a2 a2 = int (rdn() * 10 ) + 1 if a2 > 10 : a2 = 10 #取a3 a3 = a3_data[i] #计算这条数据对应的类别 c_id = 0 if a1 < = 3 and a2 > = 4 and a3 < = 50 : c_id = 0 elif a1 < = 3 and a2 > = 4 and a3 > 50 : c_id = 1 elif a1 < = 3 and a2 < 4 and a3 > 30 : c_id = 0 elif a1 < = 3 and a2 < 4 and a3 < = 30 : c_id = 1 elif a1 > 3 and a2 > = 4 and a3 < = 50 : c_id = 0 elif a1 > 3 and a2 > = 4 and a3 > 50 : c_id = 1 elif a1 > 3 and a2 < 4 and a3 > 30 : c_id = 0 elif a1 > 3 and a2 < 4 and a3 < = 30 : c_id = 1 else : print ( 'error' ) #拼合成字串 str_line = str (i) + ',' + str (a1) + ',' + str (a2) + ',' + str (a3) + ',' + str (c_id) data.append(str_line) return '\n' .join(data) |
激活函数
1
2
3
4
5
6
7
8
9
10
11
|
# -*- coding: utf-8 -*- """ created on sun dec 2 14:49:31 2018 @author: congpeiqing """ import numpy as np #sigmoid函数的导数为 f(x)*(1-f(x)) def sigmoid(x) : return 1 / ( 1 + np.exp( - x)) |
网络实现
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
|
# -*- coding: utf-8 -*- """ created on sun dec 2 14:49:31 2018 @author: congpeiqing """ from activation_funcs import sigmoid from random import random class inputnode( object ) : def __init__( self , idx) : self .idx = idx self .output = none def setinput( self , value) : self .output = value def getoutput( self ) : return self .output def refreshparas( self , p1 , p2) : pass class neurode( object ) : def __init__( self , layer_name , idx , input_nodes , activation_func = none , powers = none , bias = none) : self .idx = idx self .layer_name = layer_name self .input_nodes = input_nodes if activation_func is not none : self .activation_func = activation_func else : #默认取 sigmoid self .activation_func = sigmoid if powers is not none : self .powers = powers else : self .powers = [random() for i in range ( len ( self .input_nodes))] if bias is not none : self .bias = bias else : self .bias = random() self .output = none def getoutput( self ) : self .output = self .activation_func( sum ( map ( lambda x : x[ 0 ].getoutput() * x[ 1 ] , zip ( self .input_nodes, self .powers))) + self .bias) return self .output def refreshparas( self , err , learn_rate) : err_add = self .output * ( 1 - self .output) * err for i in range ( len ( self .input_nodes)) : #调用子节点 self .input_nodes[i].refreshparas( self .powers[i] * err_add , learn_rate) #调节参数 power_delta = learn_rate * err_add * self .input_nodes[i].output self .powers[i] + = power_delta bias_delta = learn_rate * err_add self .bias + = bias_delta class simplebp( object ) : def __init__( self , input_node_num , hidden_layer_node_num , trainning_data , test_data) : self .input_node_num = input_node_num self .input_nodes = [inputnode(i) for i in range (input_node_num)] self .hidden_layer_nodes = [neurode( 'h' , i , self .input_nodes) for i in range (hidden_layer_node_num)] self .output_node = neurode( 'o' , 0 , self .hidden_layer_nodes) self .trainning_data = trainning_data self .test_data = test_data #逐条训练 def trainbyitem( self ) : cnt = 0 while true : cnt + = 1 learn_rate = 1.0 / cnt sum_diff = 0.0 #对于每一条训练数据进行一次训练过程 for item in self .trainning_data : for i in range ( self .input_node_num) : self .input_nodes[i].setinput(item[i]) item_output = item[ - 1 ] nn_output = self .output_node.getoutput() #print('nn_output:' , nn_output) diff = (item_output - nn_output) sum_diff + = abs (diff) self .output_node.refreshparas(diff , learn_rate) #print('refreshedparas') #结束条件 print ( round (sum_diff / len ( self .trainning_data) , 4 )) if sum_diff / len ( self .trainning_data) < 0.1 : break def getaccuracy( self ) : cnt = 0 for item in self .test_data : for i in range ( self .input_node_num) : self .input_nodes[i].setinput(item[i]) item_output = item[ - 1 ] nn_output = self .output_node.getoutput() if (nn_output > 0.5 and item_output > 0.5 ) or (nn_output < 0.5 and item_output < 0.5 ) : cnt + = 1 return cnt / ( len ( self .test_data) + 0.0 ) |
主调流程
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
|
# -*- coding: utf-8 -*- """ created on sun dec 2 14:49:31 2018 @author: congpeiqing """ import os from simplebp import simplebp from gendata import gendata if not os.path.exists( 'data' ): os.makedirs( 'data' ) #构造训练和测试数据 data_file = open ( 'data/trainning_data.dat' , 'w' ) data_file.write(gendata()) data_file.close() data_file = open ( 'data/test_data.dat' , 'w' ) data_file.write(gendata()) data_file.close() #文件格式:rec_id,attr1_value,attr2_value,attr3_value,class_id #读取和解析训练数据 trainning_data_file = open ( 'data/trainning_data.dat' ) trainning_data = [] for line in trainning_data_file : line = line.strip() fld_list = line.split( ',' ) trainning_data.append( tuple ([ float (field) for field in fld_list[ 1 :]])) trainning_data_file.close() #读取和解析测试数据 test_data_file = open ( 'data/test_data.dat' ) test_data = [] for line in test_data_file : line = line.strip() fld_list = line.split( ',' ) test_data.append( tuple ([ float (field) for field in fld_list[ 1 :]])) test_data_file.close() #构造一个二分类网络 输入节点3个,隐层节点10个,输出节点一个 simple_bp = simplebp( 3 , 10 , trainning_data , test_data) #训练网络 simple_bp.trainbyitem() #测试分类准确率 print ( 'accuracy : ' , simple_bp.getaccuracy()) #训练时长比较长,准确率可以达到97% |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/conggova/article/details/77799464