当前位置:   article > 正文

深度学习入门(三):神经网络和反向传播算法_self.downstream = []记录的是什么样的数据

self.downstream = []记录的是什么样的数据

学习资料来源:零基础入门深度学习(3) - 神经网络和反向传播算法

目录

 一、神经元

二、神经网络

2.1 神经网络的定义

 2.2 神经网络的输出计算

 2.3 神经网络的训练

2.4 神经网络的实现


 一、神经元

        神经元和感知器本质上是一样的,只是神经元的激活函数是sigmoid函数tanh函数,如下图所示:

对于sigmoid函数,其定义如下:

 sigmoid函数是一个非线性函数,值域是(0,1)。函数图像如下图所示:

 同时,sigmoid函数的导数是:

 可见,sigmoid函数的导数是用sigmoid函数自身来表示。一旦计算出sigmoid函数的值,计算它的导数的值就非常方便。

        假设神经元的输入是向量x,权重向量是w,激活函数是sigmoid函数,则其输出y:

二、神经网络

2.1 神经网络的定义

神经网络:按照一定规则连接起来的多个神经元。 

全连接(full connected,FC)神经网络如下图所示:

规则如下:

  • 神经元按照层来布局。最左边的层叫做输入层(负责接收输入数据);最右边的层叫输出层(获取神经网络输出数据);输入层和输出层之间的层叫做隐藏层(对外部是不可见的)。
  • 同一层的神经元之间没有连接。
  • 第N层的每个神经元和第N-1层的所有神经元相连(full connected),第N-1层神经元的输出就是第N层神经元的输入。
  • 每个连接都有一个权值

 2.2 神经网络的输出计算

        神经网络就是一个输入向量x到输出向量y的函数,即:

       根据输入计算神经网络的输出,需要首先将输入向量x的每个元素xi的值赋给神经网络的输入层的对应神经元,然后根据y=sigmoid(wx+b)依次计算每一层的每个神经元的值,直到最后一层输出层的所有神经元的值计算完毕。最后,将输出层每个神经元的值串在一起就得到了输出向量y。

 2.3 神经网络的训练

        神经网络是一个模型,权值(模型要学习的东西)是模型的参数然而,一个神经网络的连接方式、网络的层数、每层的节点数这些参数(不是学习出来的)是人为事先设置的。对于这些人为设置的参数,我们称之为超参数(Hyper-Parameters)

神经网络的训练算法:反向传播算法(Back  Propagation)

        假设每个训练样本为(x,t),其中向量x是训练样本的特征,而t是样本的目标值。

        首先,根据神经网络的输出计算方法,用样本的特征x,计算出神经网络中每个隐藏层节点的输出ai,以及输出层每个节点的输出yi。

        然后,按照下面的方法计算出每个节点的误差项δi,即

  • 对于输出层节点i,

 其中,δi是节点i的误差项,yi是节点i的输出值,ti是样本对应于节点i的目标值。

  • 对于隐藏层节点i,

 其中,ai是节点i的输出值,wki是节点i到它的下一层节点k的连接的权重,δk是节点i的下一层节点k的误差项。

        最后,更新每个连接上的权值:

 其中wji是节点i到节点j的权重,η是学习速率,δj是节点j的误差项,xji是节点i传递给节点j的输入。

可见,计算每一个节点的误差项,需要先计算每个节点与其相连的下一层节点的误差项。这就要求误差项的计算顺序必须是从输出层开始,然后反向依次计算每个隐藏层的误差项,直到与输入层相连的那个隐藏层。这就是反向传播算法的名字的含义。当所有节点的误差项计算完毕后,我们就可以来更新所有的权重。

        因此,推导出的训练规则是根据激活函数是sigmoid函数、目标函数是平方和误差、神经网络是全连接网络、优化算法是随机梯度下降优化算法。如果激活函数、误差计算方式、网络连接结构、优化算法不同,则具体的训练规则也会不一样。

2.4 神经网络的实现

首先,做一个基本的模型,可以分解出5个领域对象来实现神经网络:

  • Network神经网络对象,提供API接口。它由若干层对象组成以及连接对象组成。
  • Layer层,由多个节点组成。
  • Node节点对象计算和记录节点自身的信息(比如输出值a、误差项δ等),以及与这个节点相关的上下游的连接。
  • Connection每个连接对象都要记录该连接的权重。
  • Connections仅仅作为Connection的集合对象,提供一些集合操作。

  1. # Node实现
  2. # 节点类,负责记录和维护节点自身信息以及与这个节点相关的上下游连接,实现输出值和误差项的计算。
  3. class Node(object):
  4. def __init__(self, layer_index, node_index):
  5. '''
  6. 构造节点对象。
  7. layer_index: 节点所属的层的编号
  8. node_index: 节点的编号
  9. '''
  10. self.layer_index = layer_index
  11. self.node_index = node_index
  12. self.downstream = []
  13. self.upstream = []
  14. self.output = 0
  15. self.delta = 0
  16. def set_output(self, output):
  17. '''
  18. 设置节点的输出值。如果节点属于输入层会用到这个函数。
  19. '''
  20. self.output = output
  21. def append_downstream_connection(self, conn):
  22. '''
  23. 添加一个到下游节点的连接
  24. '''
  25. self.downstream.append(conn)
  26. def append_upstream_connection(self, conn):
  27. '''
  28. 添加一个到上游节点的连接
  29. '''
  30. self.upstream.append(conn)
  31. def calc_output(self):
  32. '''
  33. 根据式1计算节点的输出
  34. '''
  35. output = reduce(lambda ret, conn: ret + conn.upstream_node.output * conn.weight, self.upstream, 0)
  36. self.output = sigmoid(output)
  37. def calc_hidden_layer_delta(self):
  38. '''
  39. 节点属于隐藏层时,根据式4计算delta
  40. '''
  41. downstream_delta = reduce(
  42. lambda ret, conn: ret + conn.downstream_node.delta * conn.weight,
  43. self.downstream, 0.0)
  44. self.delta = self.output * (1 - self.output) * downstream_delta
  45. def calc_output_layer_delta(self, label):
  46. '''
  47. 节点属于输出层时,根据式3计算delta
  48. '''
  49. self.delta = self.output * (1 - self.output) * (label - self.output)
  50. def __str__(self):
  51. '''
  52. 打印节点的信息
  53. '''
  54. node_str = '%u-%u: output: %f delta: %f' % (self.layer_index, self.node_index, self.output, 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. # ConstNode对象
  59. class ConstNode(object):
  60. def __init__(self, layer_index, node_index):
  61. '''
  62. 构造节点对象。
  63. layer_index: 节点所属的层的编号
  64. node_index: 节点的编号
  65. '''
  66. self.layer_index = layer_index
  67. self.node_index = node_index
  68. self.downstream = []
  69. self.output = 1
  70. def append_downstream_connection(self, conn):
  71. '''
  72. 添加一个到下游节点的连接
  73. '''
  74. self.downstream.append(conn)
  75. def calc_hidden_layer_delta(self):
  76. '''
  77. 节点属于隐藏层时,根据式4计算delta
  78. '''
  79. downstream_delta = reduce(
  80. lambda ret, conn: ret + conn.downstream_node.delta * conn.weight,
  81. self.downstream, 0.0)
  82. self.delta = self.output * (1 - self.output) * downstream_delta
  83. def __str__(self):
  84. '''
  85. 打印节点的信息
  86. '''
  87. node_str = '%u-%u: output: 1' % (self.layer_index, self.node_index)
  88. downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '')
  89. return node_str + '\n\tdownstream:' + downstream_str
  90. # Layer对象
  91. class Layer(object):
  92. def __init__(self, layer_index, node_count):
  93. '''
  94. 初始化一层
  95. layer_index: 层编号
  96. node_count: 层所包含的节点个数
  97. '''
  98. self.layer_index = layer_index
  99. self.nodes = []
  100. for i in range(node_count):
  101. self.nodes.append(Node(layer_index, i))
  102. self.nodes.append(ConstNode(layer_index, node_count))
  103. def set_output(self, data):
  104. '''
  105. 设置层的输出。当层是输入层时会用到。
  106. '''
  107. for i in range(len(data)):
  108. self.nodes[i].set_output(data[i])
  109. def calc_output(self):
  110. '''
  111. 计算层的输出向量
  112. '''
  113. for node in self.nodes[:-1]:
  114. node.calc_output()
  115. def dump(self):
  116. '''
  117. 打印层的信息
  118. '''
  119. for node in self.nodes:
  120. print node
  121. # Connection对象
  122. class Connection(object):
  123. def __init__(self, upstream_node, downstream_node):
  124. '''
  125. 初始化连接,权重初始化为是一个很小的随机数
  126. upstream_node: 连接的上游节点
  127. downstream_node: 连接的下游节点
  128. '''
  129. self.upstream_node = upstream_node
  130. self.downstream_node = downstream_node
  131. self.weight = random.uniform(-0.1, 0.1)
  132. self.gradient = 0.0
  133. def calc_gradient(self):
  134. '''
  135. 计算梯度
  136. '''
  137. self.gradient = self.downstream_node.delta * self.upstream_node.output
  138. def get_gradient(self):
  139. '''
  140. 获取当前的梯度
  141. '''
  142. return self.gradient
  143. def update_weight(self, rate):
  144. '''
  145. 根据梯度下降算法更新权重
  146. '''
  147. self.calc_gradient()
  148. self.weight += rate * self.gradient
  149. def __str__(self):
  150. '''
  151. 打印连接信息
  152. '''
  153. return '(%u-%u) -> (%u-%u) = %f' % (
  154. self.upstream_node.layer_index,
  155. self.upstream_node.node_index,
  156. self.downstream_node.layer_index,
  157. self.downstream_node.node_index,
  158. self.weight)
  159. # Connections对象
  160. class Connections(object):
  161. def __init__(self):
  162. self.connections = []
  163. def add_connection(self, connection):
  164. self.connections.append(connection)
  165. def dump(self):
  166. for conn in self.connections:
  167. print conn
  168. #Network对象
  169. class Network(object):
  170. def __init__(self, layers):
  171. '''
  172. 初始化一个全连接神经网络
  173. layers: 二维数组,描述神经网络每层节点数
  174. '''
  175. self.connections = Connections()
  176. self.layers = []
  177. layer_count = len(layers)
  178. node_count = 0;
  179. for i in range(layer_count):
  180. self.layers.append(Layer(i, layers[i]))
  181. for layer in range(layer_count - 1):
  182. connections = [Connection(upstream_node, downstream_node)
  183. for upstream_node in self.layers[layer].nodes
  184. for downstream_node in self.layers[layer + 1].nodes[:-1]]
  185. for conn in connections:
  186. self.connections.add_connection(conn)
  187. conn.downstream_node.append_upstream_connection(conn)
  188. conn.upstream_node.append_downstream_connection(conn)
  189. def train(self, labels, data_set, rate, iteration):
  190. '''
  191. 训练神经网络
  192. labels: 数组,训练样本标签。每个元素是一个样本的标签。
  193. data_set: 二维数组,训练样本特征。每个元素是一个样本的特征。
  194. '''
  195. for i in range(iteration):
  196. for d in range(len(data_set)):
  197. self.train_one_sample(labels[d], data_set[d], rate)
  198. def train_one_sample(self, label, sample, rate):
  199. '''
  200. 内部函数,用一个样本训练网络
  201. '''
  202. self.predict(sample)
  203. self.calc_delta(label)
  204. self.update_weight(rate)
  205. def calc_delta(self, label):
  206. '''
  207. 内部函数,计算每个节点的delta
  208. '''
  209. output_nodes = self.layers[-1].nodes
  210. for i in range(len(label)):
  211. output_nodes[i].calc_output_layer_delta(label[i])
  212. for layer in self.layers[-2::-1]:
  213. for node in layer.nodes:
  214. node.calc_hidden_layer_delta()
  215. def update_weight(self, rate):
  216. '''
  217. 内部函数,更新每个连接权重
  218. '''
  219. for layer in self.layers[:-1]:
  220. for node in layer.nodes:
  221. for conn in node.downstream:
  222. conn.update_weight(rate)
  223. def calc_gradient(self):
  224. '''
  225. 内部函数,计算每个连接的梯度
  226. '''
  227. for layer in self.layers[:-1]:
  228. for node in layer.nodes:
  229. for conn in node.downstream:
  230. conn.calc_gradient()
  231. def get_gradient(self, label, sample):
  232. '''
  233. 获得网络在一个样本下,每个连接上的梯度
  234. label: 样本标签
  235. sample: 样本输入
  236. '''
  237. self.predict(sample)
  238. self.calc_delta(label)
  239. self.calc_gradient()
  240. def predict(self, sample):
  241. '''
  242. 根据输入的样本预测输出值
  243. sample: 数组,样本的特征,也就是网络的输入向量
  244. '''
  245. self.layers[0].set_output(sample)
  246. for i in range(1, len(self.layers)):
  247. self.layers[i].calc_output()
  248. return map(lambda node: node.output, self.layers[-1].nodes[:-1])
  249. def dump(self):
  250. '''
  251. 打印网络信息
  252. '''
  253. for layer in self.layers:
  254. layer.dump()

向量化编程(底层算法库会针对向量运算做优化,甚至有专门的硬件比如GPU,程序效率会提升很多) 

  1. # 全连接层实现类
  2. class FullConnectedLayer(object):
  3. def __init__(self, input_size, output_size,
  4. activator):
  5. '''
  6. 构造函数
  7. input_size: 本层输入向量的维度
  8. output_size: 本层输出向量的维度
  9. activator: 激活函数
  10. '''
  11. self.input_size = input_size
  12. self.output_size = output_size
  13. self.activator = activator
  14. # 权重数组W
  15. self.W = np.random.uniform(-0.1, 0.1,
  16. (output_size, input_size))
  17. # 偏置项b
  18. self.b = np.zeros((output_size, 1))
  19. # 输出向量
  20. self.output = np.zeros((output_size, 1))
  21. def forward(self, input_array):
  22. '''
  23. 前向计算
  24. input_array: 输入向量,维度必须等于input_size
  25. '''
  26. # 式2
  27. self.input = input_array
  28. self.output = self.activator.forward(
  29. np.dot(self.W, input_array) + self.b)
  30. def backward(self, delta_array):
  31. '''
  32. 反向计算W和b的梯度
  33. delta_array: 从上一层传递过来的误差项
  34. '''
  35. # 式8
  36. self.delta = self.activator.backward(self.input) * np.dot(
  37. self.W.T, delta_array)
  38. self.W_grad = np.dot(delta_array, self.input.T)
  39. self.b_grad = delta_array
  40. def update(self, learning_rate):
  41. '''
  42. 使用梯度下降算法更新权重
  43. '''
  44. self.W += learning_rate * self.W_grad
  45. self.b += learning_rate * self.b_grad
  46. # 修改netowork
  47. # Sigmoid激活函数类
  48. class SigmoidActivator(object):
  49. def forward(self, weighted_input):
  50. return 1.0 / (1.0 + np.exp(-weighted_input))
  51. def backward(self, output):
  52. return output * (1 - output)
  53. # 神经网络类
  54. class Network(object):
  55. def __init__(self, layers):
  56. '''
  57. 构造函数
  58. '''
  59. self.layers = []
  60. for i in range(len(layers) - 1):
  61. self.layers.append(
  62. FullConnectedLayer(
  63. layers[i], layers[i+1],
  64. SigmoidActivator()
  65. )
  66. )
  67. def predict(self, sample):
  68. '''
  69. 使用神经网络实现预测
  70. sample: 输入样本
  71. '''
  72. output = sample
  73. for layer in self.layers:
  74. layer.forward(output)
  75. output = layer.output
  76. return output
  77. def train(self, labels, data_set, rate, epoch):
  78. '''
  79. 训练函数
  80. labels: 样本标签
  81. data_set: 输入样本
  82. rate: 学习速率
  83. epoch: 训练轮数
  84. '''
  85. for i in range(epoch):
  86. for d in range(len(data_set)):
  87. self.train_one_sample(labels[d],
  88. data_set[d], rate)
  89. def train_one_sample(self, label, sample, rate):
  90. self.predict(sample)
  91. self.calc_gradient(label)
  92. self.update_weight(rate)
  93. def calc_gradient(self, label):
  94. delta = self.layers[-1].activator.backward(
  95. self.layers[-1].output
  96. ) * (label - self.layers[-1].output)
  97. for layer in self.layers[::-1]:
  98. layer.backward(delta)
  99. delta = layer.delta
  100. return delta
  101. def update_weight(self, rate):
  102. for layer in self.layers:
  103. layer.update(rate)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/59934?site
推荐阅读
相关标签
  

闽ICP备14008679号