概述

本文demo非常适合入门AI与深度学习的同学,从最基础的知识讲起,只要有一点点的高等数学、统计学、矩阵的相关知识,相信大家完全可以看明白。程序的编写不借助任何第三方的深度学习库,从最底层写起。 第一,本文介绍了什么是神经网络,神经网络的特点,神经网络中的BP算法,神经网络的训练方法,神经网络的激活函数,损失函数、权值初始化方法、权值的正则化机制等一系列知识。 第二,在此基础上,使用最基础的python语法来实现一个神经网络框架,利用此神经网络框架,可以搭建自己的深度神经网络,同时大家也可以根据自己的需求添加其它功能。为了方便大家阅读源码与使用,包含了一份简单的说明文档。 第三,我们基于该框架,搭建一个深层神经网络,实现手写字体的分类任务。

详细

一、基础知识介绍

神经网络基础知识的介绍部分包含了大量公式及图,使用网站的在线编辑器,实现是力不从心。我写了13页的word文档,放在了解压包中,大家下载来看吧,我录了一个视频,大家可以大致浏览一下。

二、Python代码实现神经网络框架

  如果大家之前对神经网络不了解的话,在看这部分内容之前,一定要掌握第一部分的基础内容,否则的话,你会看不懂源代码的,因为很多代码都是根根据公式才能写出来的。

在此处,我们把一个深度神经网络可以分为许多层,包括数据的输入层、全连接层、激活函数层、损失函数层等,另外还可以加入dropout层。如果想构建卷积神经网络的话,还可以加入卷积层、池化层等。本demo实现的神经网络框架就是基于分层结构,把每一层实现之后,大家就可以根据自己的需要,搭建自己的神经网络了。

本框架包括的核心模块及作用:

layer模块:里面定义组成神经网络各层的作用,包括数据输入层、全连接层、激活函数层、损失函数层等。

function_for_layer模块:里面定义了激活函数、损失函数、权值初始化方法等。

update_method模块:学习率的更新机制、权值的更新机制(如批量随机梯度下降法)等。

net模块:大家可以根据自己的需要,在这里定义自己的神经网络。

图1给出了神经网络框架的示意图。

另外,在上传的压缩包里面,还有一份关于神经网络框架的说明文档,大家可以根据看着说明文档读源码。我录了一个小视频 ,大家可以浏览一下。

layer模块:

数据输入层:

  1. class data:
  2. def __init__(self):
  3. self.data_sample = 0
  4. self.data_label = 0
  5. self.output_sample = 0
  6. self.output_label = 0
  7. self.point = 0 #用于记住下一次pull数据的地方;
  8.  
  9. def get_data(self, sample, label): # sample 每一行表示一个样本数据, label的每一行表示一个样本的标签.
  10. self.data_sample = sample
  11. self.data_label = label
  12.  
  13. def shuffle(self): # 用于打乱顺序;
  14. random_sequence = random.sample(np.arange(self.data_sample.shape[0]), self.data_sample.shape[0])
  15. self.data_sample = self.data_sample[random_sequence]
  16. self.data_label = self.data_label[random_sequence]
  17.  
  18. def pull_data(self): #把数据推向输出
  19. start = self.point
  20. end = start + batch_size
  21. output_index = np.arange(start, end)
  22. if end > self.data_sample.shape[0]:
  23. end = end - self.data_sample.shape[0]
  24. output_index = np.append(np.arange(start, self.data_sample.shape[0]), np.arange(0, end))
  25. self.output_sample = self.data_sample[output_index]
  26. self.output_label = self.data_label[output_index]
  27. self.point = end % self.data_sample.shape[0]

全连接层:

  1. class fully_connected_layer:
  2. def __init__(self, num_neuron_inputs, num_neuron_outputs):
  3. self.num_neuron_inputs = num_neuron_inputs
  4. self.num_neuron_outputs = num_neuron_outputs
  5. self.inputs = np.zeros((batch_size, num_neuron_inputs))
  6. self.outputs = np.zeros((batch_size, num_neuron_outputs))
  7. self.weights = np.zeros((num_neuron_inputs, num_neuron_outputs))
  8. self.bias = np.zeros(num_neuron_outputs)
  9. self.weights_previous_direction = np.zeros((num_neuron_inputs, num_neuron_outputs))
  10. self.bias_previous_direction = np.zeros(num_neuron_outputs)
  11. self.grad_weights = np.zeros((batch_size, num_neuron_inputs, num_neuron_outputs))
  12. self.grad_bias = np.zeros((batch_size, num_neuron_outputs))
  13. self.grad_inputs = np.zeros((batch_size, num_neuron_inputs))
  14. self.grad_outputs = np.zeros((batch_size,num_neuron_outputs))
  15.  
  16. def initialize_weights(self):
  17. self.weights = ffl.xavier(self.num_neuron_inputs, self.num_neuron_outputs)
  18.  
  19. # 在正向传播过程中,用于获取输入;
  20. def get_inputs_for_forward(self, inputs):
  21. self.inputs = inputs
  22.  
  23. def forward(self):
  24. self.outputs = self.inputs .dot(self.weights) + np.tile(self.bias, (batch_size, 1))
  25.  
  26. # 在反向传播过程中,用于获取输入;
  27. def get_inputs_for_backward(self, grad_outputs):
  28. self.grad_outputs = grad_outputs
  29.  
  30. def backward(self):
  31. #求权值的梯度,求得的结果是一个三维的数组,因为有多个样本;
  32. for i in np.arange(batch_size):
  33. self.grad_weights[i,:] = np.tile(self.inputs[i,:], (1, 1)).T \
  34. .dot(np.tile(self.grad_outputs[i, :], (1, 1))) + \
  35. self.weights * weights_decay
  36. #求求偏置的梯度;
  37. self.grad_bias = self.grad_outputs
  38. #求 输入的梯度;
  39. self.grad_inputs = self.grad_outputs .dot(self.weights.T)
  40.  
  41. def update(self):
  42. #权值与偏置的更新;
  43. grad_weights_average = np.mean(self.grad_weights, 0)
  44. grad_bias_average = np.mean(self.grad_bias, 0)
  45. (self.weights, self.weights_previous_direction) = update_function(self.weights,
  46. grad_weights_average,
  47. self.weights_previous_direction)
  48. (self.bias, self.bias_previous_direction) = update_function(self.bias,
  49. grad_bias_average,
  50. self.bias_previous_direction)

激活函数层:

  1. class activation_layer:
  2. def __init__(self, activation_function_name):
  3. if activation_function_name == 'sigmoid':
  4. self.activation_function = ffl.sigmoid
  5. self.der_activation_function = ffl.der_sigmoid
  6. elif activation_function_name == 'tanh':
  7. self.activation_function = ffl.tanh
  8. self.der_activation_function = ffl.der_tanh
  9. elif activation_function_name == 'relu':
  10. self.activation_function = ffl.relu
  11. self.der_activation_function = ffl.der_relu
  12. else:
  13. print '输入的激活函数不对啊'
  14. self.inputs = 0
  15. self.outputs = 0
  16. self.grad_inputs = 0
  17. self.grad_outputs = 0
  18.  
  19. def get_inputs_for_forward(self, inputs):
  20. self.inputs = inputs
  21.  
  22. def forward(self):
  23. #需要激活函数
  24. self.outputs = self.activation_function(self.inputs)
  25.  
  26. def get_inputs_for_backward(self, grad_outputs):
  27. self.grad_outputs = grad_outputs
  28.  
  29. def backward(self):
  30. #需要激活函数的导数
  31. self.grad_inputs = self.grad_outputs * self.der_activation_function(self.inputs)

损失函数层:

  1. class loss_layer:
  2. def __init__(self, loss_function_name):
  3. self.inputs = 0
  4. self.loss = 0
  5. self.accuracy = 0
  6. self.label = 0
  7. self.grad_inputs = 0
  8. if loss_function_name == 'SoftmaxWithLoss':
  9. self.loss_function =ffl.softmaxwithloss
  10. self.der_loss_function =ffl.der_softmaxwithloss
  11. elif loss_function_name == 'LeastSquareError':
  12. self.loss_function =ffl.least_square_error
  13. self.der_loss_function =ffl.der_least_square_error
  14. else:
  15. print '输入的损失函数不对吧,别继续了,重新输入吧'
  16.  
  17. def get_label_for_loss(self, label):
  18. self.label = label
  19.  
  20. def get_inputs_for_loss(self, inputs):
  21. self.inputs = inputs
  22.  
  23. def compute_loss_and_accuracy(self):
  24. #计算正确率
  25. if_equal = np.argmax(self.inputs, 1) == np.argmax(self.label, 1)
  26. self.accuracy = np.sum(if_equal) / batch_size
  27. #计算训练误差
  28. self.loss = self.loss_function(self.inputs, self.label)
  29.  
  30. def compute_gradient(self):
  31. self.grad_inputs = self.der_loss_function(self.inputs, self.label)

function_for_layer模块:

激活函数的定义:

  1. # sigmoid函数及其导数的定义
  2. def sigmoid(x):
  3. return 1 / (1 + np.exp(-x))
  4. def der_sigmoid(x):
  5. return sigmoid(x) * (1 - sigmoid(x))
  6.  
  7. # tanh函数及其导数的定义
  8. def tanh(x):
  9. return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
  10. def der_tanh(x):
  11. return 1 - tanh(x) * tanh(x)
  12.  
  13. # ReLU函数及其导数的定义
  14. def relu(x):
  15. temp = np.zeros_like(x)
  16. if_bigger_zero = (x > temp)
  17. return x * if_bigger_zero
  18. def der_relu(x):
  19. temp = np.zeros_like(x)
  20. if_bigger_equal_zero = (x >= temp) #在零处的导数设为1
  21. return if_bigger_equal_zero * np.ones_like(x)

损失函数的定义:

  1. # SoftmaxWithLoss函数及其导数的定义
  2. def softmaxwithloss(inputs, label):
  3. temp1 = np.exp(inputs)
  4. probability = temp1 / (np.tile(np.sum(temp1, 1), (inputs.shape[1], 1))).T
  5. temp3 = np.argmax(label, 1) #纵坐标
  6. temp4 = [probability[i, j] for (i, j) in zip(np.arange(label.shape[0]), temp3)]
  7. loss = -1 * np.mean(np.log(temp4))
  8. return loss
  9. def der_softmaxwithloss(inputs, label):
  10. temp1 = np.exp(inputs)
  11. temp2 = np.sum(temp1, 1) #它得到的是一维的向量;
  12. probability = temp1 / (np.tile(temp2, (inputs.shape[1], 1))).T
  13. gradient = probability - label
  14. return gradient

权值初始化方法:

  1. # xavier 初始化方法
  2. def xavier(num_neuron_inputs, num_neuron_outputs):
  3. temp1 = np.sqrt(6) / np.sqrt(num_neuron_inputs+ num_neuron_outputs + 1)
  4. weights = stats.uniform.rvs(-temp1, 2 * temp1, (num_neuron_inputs, num_neuron_outputs))
  5. return weights

update_method模块:

学习率的更新机制:

  1. #定义一些需要的全局变量
  2. momentum = 0.9
  3. base_lr = 0 # 在建造net是对它初始化;
  4. iteration = -1 # 它常常需要在训练过程中修改
  5.  
  6. ########################### 定义学习率的变化机制函数 ####################################
  7.  
  8. # inv方法
  9. def inv(gamma = 0.0005, power = 0.75):
  10. if iteration == -1:
  11. assert False, '需要在训练过程中,改变update_method 模块里的 iteration 的值'
  12. return base_lr * np.power((1 + gamma * iteration), -power)
  13.  
  14. # 固定方法
  15. def fixed():
  16. return base_lr

批量随机梯度下降法:

  1. # 基于批量的随机梯度下降法
  2. def batch_gradient_descent(weights, grad_weights, previous_direction):
  3. lr = inv()
  4. direction = momentum * previous_direction + lr * grad_weights
  5. weights_now = weights - direction
  6. return (weights_now, direction)

net模块:

例如定义一个四层的神经网络:

  1. #搭建一个四层的神经网络;
  2. self.inputs_train = layer.data() # 训练样本的输入层
  3. self.inputs_test = layer.data() # 测试样本的输入层
  4.  
  5. self.fc1 = layer.fully_connected_layer(784, 50)
  6. self.ac1 = layer.activation_layer('tanh')
  7. self.fc2 = layer.fully_connected_layer(50, 50)
  8. self.ac2 = layer.activation_layer('tanh')
  9. self.fc3 = layer.fully_connected_layer(50, 10)
  10. self.loss = layer.loss_layer('SoftmaxWithLoss')

定义网络的一些其它功能接口,例如载入训练样本与测试样本:

  1. def load_sample_and_label_train(self, sample, label):
  2. self.inputs_train.get_data(sample, label)
  3. def load_sample_and_label_test(self, sample, label):
  4. self.inputs_test.get_data(sample, label)

定义网络的初始化接口:

  1. def initial(self):
  2. self.fc1.initialize_weights()
  3. self.fc2.initialize_weights()
  4. self.fc3.initialize_weights()

定义在训练过程中网络的前向传播与反向传播:

  1. def forward_train(self):
  2. self.inputs_train.pull_data()
  3.  
  4. self.fc1.get_inputs_for_forward(self.inputs_train.outputs)
  5. self.fc1.forward()
  6. self.ac1.get_inputs_for_forward(self.fc1.outputs)
  7. self.ac1.forward()
  8.  
  9. self.fc2.get_inputs_for_forward(self.ac1.outputs)
  10. self.fc2.forward()
  11. self.ac2.get_inputs_for_forward(self.fc2.outputs)
  12. self.ac2.forward()
  13.  
  14. self.fc3.get_inputs_for_forward(self.ac2.outputs)
  15. self.fc3.forward()
  16.  
  17. self.loss.get_inputs_for_loss(self.fc3.outputs)
  18. self.loss.get_label_for_loss(self.inputs_train.output_label)
  19. self.loss.compute_loss_and_accuracy()
  20.  
  21. def backward_train(self):
  22. self.loss.compute_gradient()
  23. self.fc3.get_inputs_for_backward(self.loss.grad_inputs)
  24. self.fc3.backward()
  25. self.ac2.get_inputs_for_backward(self.fc3.grad_inputs)
  26. self.ac2.backward()
  27. self.fc2.get_inputs_for_backward(self.ac2.grad_inputs)
  28. self.fc2.backward()
  29. self.ac1.get_inputs_for_backward(self.fc2.grad_inputs)
  30. self.ac1.backward()
  31. self.fc1.get_inputs_for_backward(self.ac1.grad_inputs)
  32. self.fc1.backward()

定义在测试过程中的网络正向传播:

  1. def forward_test(self):
  2. self.inputs_test.pull_data()
  3.  
  4. self.fc1.get_inputs_for_forward(self.inputs_test.outputs)
  5. self.fc1.forward()
  6. self.ac1.get_inputs_for_forward(self.fc1.outputs)
  7. self.ac1.forward()
  8.  
  9. self.fc2.get_inputs_for_forward(self.ac1.outputs)
  10. self.fc2.forward()
  11. self.ac2.get_inputs_for_forward(self.fc2.outputs)
  12. self.ac2.forward()
  13.  
  14. self.fc3.get_inputs_for_forward(self.ac2.outputs)
  15. self.fc3.forward()
  16.  
  17. self.loss.get_inputs_for_loss(self.fc3.outputs)
  18. self.loss.get_label_for_loss(self.inputs_test.output_label)
  19. self.loss.compute_loss_and_accuracy()

定义权值与梯度的更新:

  1. def update(self):
  2. self.fc1.update()
  3. self.fc2.update()
  4. self.fc3.update()

三、使用在net模块定义好的神经网络识别手写字体

在第二部分中的net模块中,我们定义了一个784*50*50*10的神经网络,训练该神经网络识别手写体数字。

手写体数字简介:来自Yann LeCun 等人维护一个手写数字集,训练样本包括60000个,测试样本为10000个,可以在官网http://yann.lecun.com/exdb/mnist/index.html下载。 但是官网的数据为二进制的数据,不方便用,不过大家不用但心,我已经把它转化为了matlab中常用的.mat格式的数据,下载压缩包/demo/data.mat中查看。 手写字体长这样子:

写一个train.py文件,使用它来训练神经网络并测试。

  1. # 导入数据;
  2. data = scipy.io.loadmat('data.mat')
  3. train_label = data['train_label']
  4. train_data = data['train_data']
  5. test_label = data['test_label']
  6. test_data = data['test_data']
  7.  
  8. #一些相关的重要参数
  9. num_train = 800
  10. lr = 0.1
  11. weight_decay = 0.001
  12. train_batch_size = 100
  13. test_batch_size = 10000
  14.  
  15. # 创建网络并加载样本
  16. solver = net.net(train_batch_size, lr, weight_decay)
  17. solver.load_sample_and_label_train(train_data, train_label)
  18. solver.load_sample_and_label_test(test_data, test_label)
  19. # 初始化权值;
  20. solver.initial()
  21.  
  22. # 用于存放训练误差
  23. train_error = np.zeros(num_train)
  24. # 训练
  25. for i in range(num_train):
  26. print '第', i, '次迭代'
  27. net.layer.update_method.iteration = i
  28. solver.forward_train()
  29. solver.backward_train()
  30. solver.update()
  31. train_error[i] = solver.loss.loss
  32.  
  33. plt.plot(train_error)
  34. plt.show()
  35. #测试
  36. solver.turn_to_test(test_batch_size)
  37. solver.forward_test()
  38. print '测试样本的识别率为:', solver.loss.accuracy

运行train.py程序,得到:

在网络训练过程中,训练误差的下降曲线为:

测试样本 的识别率为:

当然,大家可以通过调节参数来调高识别率。

四、项目文件目录截图

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

深层神经网络框架的python实现的更多相关文章

  1. TensorFlow(实战深度学习框架)----深层神经网络(第四章)

    深层神经网络可以解决部分浅层神经网络解决不了的问题. 神经网络的优化目标-----损失函数 深度学习:一类通过多层非线性变化对高复杂性数据建模算法的合集.(两个重要的特性:多层和非线性) 线性模型的最 ...

  2. Caffe(卷积神经网络框架)介绍

    Caffe(卷积神经网络框架)Caffe,全称Convolution Architecture For Feature Extraction caffe是一个清晰,可读性高,快速的深度学习框架.作者是 ...

  3. 一个可扩展的深度学习框架的Python实现(仿keras接口)

    一个可扩展的深度学习框架的Python实现(仿keras接口) 动机 keras是一种非常优秀的深度学习框架,其具有较好的易用性,可扩展性.keras的接口设计非常优雅,使用起来非常方便.在这里,我将 ...

  4. tensorFlow(五)深层神经网络

    TensorFlow基础见前博客 上实例: MNIST 数据集介绍 MNIST 是一个手写阿拉伯数字的数据集. 其中包含有 60000 个已经标注了的训练集,还有 10000 个用于测试的测试集. 本 ...

  5. TensorFlow学习笔记——深层神经网络的整理

    维基百科对深度学习的精确定义为“一类通过多层非线性变换对高复杂性数据建模算法的合集”.因为深层神经网络是实现“多层非线性变换”最常用的一种方法,所以在实际中可以认为深度学习就是深度神经网络的代名词.从 ...

  6. 用TensorFlow搭建一个万能的神经网络框架(持续更新)

    我一直觉得TensorFlow的深度神经网络代码非常困难且繁琐,对TensorFlow搭建模型也十分困惑,所以我近期阅读了大量的神经网络代码,终于找到了搭建神经网络的规律,各位要是觉得我的文章对你有帮 ...

  7. DeepLearning.ai学习笔记(一)神经网络和深度学习--Week4深层神经网络

    一.深层神经网络 深层神经网络的符号与浅层的不同,记录如下: 用\(L\)表示层数,该神经网络\(L=4\) \(n^{[l]}\)表示第\(l\)层的神经元的数量,例如\(n^{[1]}=n^{[2 ...

  8. [DeeplearningAI笔记]改善深层神经网络_深度学习的实用层面1.10_1.12/梯度消失/梯度爆炸/权重初始化

    觉得有用的话,欢迎一起讨论相互学习~Follow Me 1.10 梯度消失和梯度爆炸 当训练神经网络,尤其是深度神经网络时,经常会出现的问题是梯度消失或者梯度爆炸,也就是说当你训练深度网络时,导数或坡 ...

  9. Serpent.AI – 游戏代理框架(Python)

    Serpent.AI - 游戏代理框架(Python) Serpent.AI是一个简单而强大的新颖框架,可帮助开发人员创建游戏代理.将您拥有的任何视频游戏变成一个成熟的实验的沙箱环境,所有这些都是熟悉 ...

随机推荐

  1. 2349 Arctic Network(中文版)

    试题描述: 国防部希望通过无线网络连接几个北方前哨基地. 在建立网络时将使用两种不同的通信技术:每个前哨基站都将拥有无线电收发器,另外还有一些前哨卫星通道. 任何带卫星频道的两个前哨都可以通过卫星进行 ...

  2. vultr购买主机前的测速地址

    https://www.vultr.com/faq/ 拉倒最下面,有个地区测速,每个点开之后ping,看延迟再进行购买,因为对应不同的宽带速度不一样. 参考: https://pdf-lib.org/ ...

  3. IOS定位核心与地图

    IOS定位核心与地图                 Core Location以及Map框架包通常能给我们的应用程序添加定位和地图相关的服务.Core Location框架包通常是使用硬件设备来进行 ...

  4. 字符串型MySQL查询条件需要注意的一点

    最近在工作中遇到了数据库服务器产生很多读写队列的问题,于是要求大家开始优化我们的SQL语句. 下面是查询quotedata_history表中的code字段的SQL语句,其中code字段的类型是var ...

  5. Windows Embedded Compact 7网络编程概述(下)

    11.1.1 Select I/O模型 在Windows CE中,Select模型是唯一被支持的I/O模型.Select I/O模型就是利用select函数对I/O进行管理. 函数select的功能在 ...

  6. linux之inode

    一.inode是什么? 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector).每个扇区储存512字节(相当于0.5KB). 操作系统读取硬盘的时候,不会 ...

  7. React事件初探

    作者:朱灵子 React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库.创造 React 是为了解决一个问题:构建随着时间数据不断变化的大规模应用程序 ...

  8. 解决 Operation must use an updateable query

    在asp.net 开发环境下,用jet 4.0连接access数据库, 有时会产生Operation must use an updateable query,这是由于相关用户mdb文件没有Modif ...

  9. 将matlab的figure保存为pdf,避免图片太大缺失

    有时画的matlab图太大,或者有太多的子图,导致图太宽,如果直接保存成pdf的话,会导致左右边丢失,显示不下.一个有效又简单的办法是:   1.在matlab figure里面,Edit -> ...

  10. HBase系统架构及数据结构(转)

    原文链接:Hbase系统架构及数据结构 HBase中的表一般有这样的特点: 1 大:一个表可以有上亿行,上百万列 2 面向列:面向列(族)的存储和权限控制,列(族)独立检索. 3 稀疏:对于为空(nu ...