neural_network.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import random
  2. from numpy import *
  3. from functools import reduce
  4. """
  5. Network 神经网络对象,提供API接口。它由若干层对象组成以及连接对象组成。
  6. Layer 层对象,由多个节点组成。
  7. Node 节点对象计算和记录节点自身的信息(比如输出值、误差项等),以及与这个节点相关的上下游的连接。
  8. Connection 每个连接对象都要记录该连接的权重。
  9. Connections 仅仅作为Connection的集合对象,提供一些集合操作。
  10. """
  11. class Node(object):
  12. def __init__(self, layer_id, node_id):
  13. """ 构造节点对象 """
  14. self.layer_id = layer_id # 节点所属的层的编号
  15. self.node_id = node_id # 节点的编号
  16. self.upstream = [] # 上游
  17. self.downstream = [] # 下游
  18. self.out = 0. # 输出值
  19. self.delta = 0. # 误差项
  20. def set_out(self, out):
  21. """ 设置设置输出值 (输入层用) """
  22. self.out = out
  23. def add_connection_down(self, conn):
  24. """ 添加一个到下游节点的连接 """
  25. self.downstream.append(conn)
  26. def add_connection_up(self, conn):
  27. """ 添加一个到上游节点的连接 """
  28. self.upstream.append(conn)
  29. def calc_out(self):
  30. """节点输出公式:y=sigmoid(w1x1+w2x2+...+b)
  31. 其中b可以看做x=1的wx
  32. :return:
  33. """
  34. out = reduce(lambda ret, conn: ret + conn.node_up.out * conn.w, self.upstream, 0.)
  35. # sigmoid
  36. self.out = 1. / (1. + exp(-out))
  37. def calc_hidden_layer_delta(self):
  38. """ 计算隐藏层的误差项
  39. 公式 a(1-a)reduce(w * 下层误差项)
  40. 其中 a是输出值 w是权重 reduce是和式
  41. :return:
  42. """
  43. down_delta = reduce(lambda ret, conn: ret + conn.node_down.delta * conn.w, self.downstream, 0.0)
  44. self.delta = self.out * (1 - self.out) * down_delta
  45. def calc_out_layer_delta(self, t):
  46. """ 计算输出层的误差项
  47. 公式 y(1-y)(t-y)
  48. 其中 y是输出值 t是目标值
  49. :return:
  50. """
  51. self.delta = self.out * (1 - self.out) * (t - self.out)
  52. def __str__(self):
  53. """ 打印节点信息 """
  54. node_str = '%u-%u: out: %f delta: %f' % (self.layer_id, self.node_id, self.out, self.delta)
  55. downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '')
  56. upstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.upstream, '')
  57. return node_str + '\n\tdownstream:' + downstream_str + '\n\tupstream:' + upstream_str
  58. class ConstNode(object):
  59. """ 输出恒为1的节点,计算偏置项需要 """
  60. def __init__(self, layer_id, node_id):
  61. self.layer_id = layer_id # 节点所属的层的编号
  62. self.node_id = node_id # 节点的编号
  63. self.downstream = [] # 下游
  64. self.out = 1. # 输出值恒为1
  65. def add_connection_down(self, conn):
  66. """ 添加一个到下游节点的连接 """
  67. self.downstream.append(conn)
  68. def calc_hidden_layer_delta(self):
  69. """ 计算隐藏层的误差项
  70. 公式 a(1-a)reduce(w * 下层误差项)
  71. 其中 a是输出值 w是权重 reduce是和式
  72. :return:
  73. """
  74. down_delta = reduce(lambda ret, conn: ret + conn.node_down.delta * conn.w, self.downstream, 0.)
  75. self.delta = self.out * (1. - self.out) * down_delta
  76. def __str__(self):
  77. """ 打印节点信息 """
  78. node_str = '%u-%u: output: 1' % (self.layer_id, self.node_id)
  79. downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '')
  80. return node_str + '\n\tdownstream:' + downstream_str
  81. class Layer(object):
  82. """ 初始化一层,提供对Node集合的操作 """
  83. def __init__(self, layer_id, node_num):
  84. self.layer_id = layer_id
  85. self.nodes = [Node(layer_id, i) for i in range(node_num)]
  86. self.nodes.append(ConstNode(layer_id, node_num))
  87. def set_out(self, input_shape):
  88. """ 设置层的输出。当层是输入层时会用到。 """
  89. for i in range(len(input_shape)):
  90. self.nodes[i].set_out(input_shape[i])
  91. def calc_out(self):
  92. """ 计算层的输出,除了输入层 """
  93. for node in self.nodes[:-1]:
  94. node.calc_out()
  95. def show(self):
  96. """ 显示层信息 """
  97. for node in self.nodes:
  98. print(node)
  99. class Connection(object):
  100. """ 记录连接的权重,以及这个连接所关联的上下游节点 """
  101. def __init__(self, node_up, node_down):
  102. self.node_up = node_up # 上游节点
  103. self.node_down = node_down # 下游节点
  104. self.w = random.uniform(-.1, .1) # 权重初始化一个很小的随机数
  105. self.gradient = .0 # 梯度
  106. def calc_gradient(self):
  107. """ 计算梯度 """
  108. self.gradient = self.node_up.out * self.node_down.delta
  109. def get_gradient(self):
  110. """ 获取梯度 """
  111. return self.gradient
  112. def update_w(self, rate):
  113. """ 梯度下降更新权重 """
  114. self.calc_gradient()
  115. self.w += rate * self.gradient
  116. def __str__(self):
  117. """ 打印连接信息 """
  118. return '(%u-%u) -> (%u-%u) = %f' % (
  119. self.node_up.layer_id,
  120. self.node_up.node_id,
  121. self.node_down.layer_id,
  122. self.node_down.node_id,
  123. self.w)
  124. class Connections(object):
  125. """ 提供Connection集合操作 """
  126. def __init__(self):
  127. self.connections = []
  128. def add_connection(self, conn):
  129. """ 添加连接 """
  130. self.connections.append(conn)
  131. def show(self):
  132. """ 显示连接 """
  133. for conn in self.connections:
  134. print(conn)
  135. class Network(object):
  136. """ 神经网络对象,提供API接口。它由若干层对象组成以及连接对象组成。 """
  137. def __init__(self, layers):
  138. """初始化一个全连接神经网络
  139. :param layers: 描述神经网络每层节点数 例: [2,3,2]
  140. """
  141. self.connections = Connections()
  142. self.layers = []
  143. layer_count = len(layers)
  144. node_count = 0
  145. # 添加层、节点
  146. for i in range(layer_count):
  147. self.layers.append(Layer(i, layers[i]))
  148. # 添加节点连接信息,连接数层数 -1
  149. for layer in range(layer_count - 1):
  150. connections = []
  151. # 全连接
  152. for node_down in self.layers[layer + 1].nodes[:-1]:
  153. for node_up in self.layers[layer].nodes:
  154. connections.append(Connection(node_up, node_down))
  155. for conn in connections:
  156. self.connections.add_connection(conn)
  157. conn.node_down.add_connection_up(conn)
  158. conn.node_up.add_connection_down(conn)
  159. def fix(self, x, y, rate, iteration):
  160. """ 训练神经网络
  161. :param x: 训练样本特征。
  162. :param y: 训练样本标签。
  163. :param rate: 速率
  164. :param iteration: 训练次数
  165. :return:
  166. """
  167. for i in range(iteration):
  168. for d in range(len(x)):
  169. self.__train_one_sample(x[d], y[d], rate)
  170. def __train_one_sample(self, x, y, rate):
  171. """ 用一个样本训练网络 """
  172. self.predict(x)
  173. self.__calc_delta(y)
  174. self.__update_w(rate)
  175. def predict(self, x):
  176. """ 根据输入的样本预测输出值 """
  177. self.layers[0].set_out(x)
  178. # 逐层计算
  179. for i in range(1, len(self.layers)):
  180. self.layers[i].calc_out()
  181. return list(map(lambda node: node.out, self.layers[-1].nodes[:-1]))
  182. def __calc_delta(self, y):
  183. """ 计算每个节点的误差项 """
  184. # 计算输出层的误差项
  185. nodes_out = self.layers[-1].nodes
  186. for i in range(len(y)):
  187. nodes_out[i].calc_out_layer_delta(y[i])
  188. # 计算隐藏层误差项
  189. for layer in self.layers[-2::-1]:
  190. for node in layer.nodes:
  191. node.calc_hidden_layer_delta()
  192. def __update_w(self, rate):
  193. """ 更新每个连接权重 """
  194. for layer in self.layers[:-1]:
  195. for node in layer.nodes:
  196. for conn in node.downstream:
  197. conn.update_w(rate)
  198. def __calc_gradient(self):
  199. """ 计算每个连接的梯度 """
  200. for layer in self.layers[:-1]:
  201. for node in layer.nodes:
  202. for conn in node.downstream:
  203. conn.calc_gradient()
  204. def get_gradient(self, x, y):
  205. """ 获得网络在一个样本下,每个连接上的梯度 """
  206. self.predict(x)
  207. self.__calc_delta(y)
  208. self.__calc_gradient()
  209. def show(self):
  210. """ 打印网络信息 """
  211. for layer in self.layers:
  212. layer.show()