欢迎访问个人博客网站获取更多文章:

https://beityluo.space

  • 以下的文字介绍在仓库中的README.md文件中有相同内容


神经网络框架使用方法及设计思想

  • 在框架上基本模仿pytorch,用以学习神经网络的基本算法,如前向传播、反向传播、各种层、各种激活函数
  • 采用面向对象的思想进行编程,思路较为清晰
  • 想要自己手写神经网络的同学们可以参考一下
  • 代码大体框架较为清晰,但不否认存在丑陋的部分,以及对于pytorch的拙劣模仿

项目介绍

  • MINST_recognition:

    • 手写数字识别,使用MINST数据集

    • 训练30轮可以达到93%准确度,训练500轮左右达到95%准确度无法继续上升

  • RNN_sin_to_cos:

    • 使用循环神经网络RNN,用\(sin\)的曲线预测\(cos\)的曲线

    • 目前仍有bug,无法正常训练

框架介绍

  • 与框架有关的代码都放在了mtorch文件夹中

  • 使用流程

    • pytorch相似,需要定义自己的神经网络、损失函数、梯度下降的优化算法等等

    • 在每一轮的训练中,先获取样本输入将其输入到自己的神经网络中获取输出。然后将预测结果和期望结果交给损失函数计算loss,并通过loss进行梯度的计算,最后通过优化器对神经网络的参数进行更新。

    • 结合代码理解更佳:

    • 以下是使用MINST数据集的手写数字识别的主体代码

    	# 定义网络 define neural network
    class DigitModule(Module):
    def __init__(self):
    # 计算顺序就会按照这里定义的顺序进行
    sequential = Sequential([
    layers.Linear2(in_dim=ROW_NUM * COLUM_NUM, out_dim=16, coe=2),
    layers.Relu(16),
    layers.Linear2(in_dim=16, out_dim=16, coe=2),
    layers.Relu(16),
    layers.Linear2(in_dim=16, out_dim=CLASS_NUM, coe=1),
    layers.Sigmoid(CLASS_NUM)
    ])
    super(DigitModule, self).__init__(sequential) module = DigitModule() # 创建模型 create module
    loss_func = SquareLoss(backward_func=module.backward) # 定义损失函数 define loss function
    optimizer = SGD(module, lr=learning_rate) # 定义优化器 define optimizer for i in range(EPOCH_NUM): # 共训练EPOCH_NUM轮
    trainning_loss = 0 # 计算一下当前一轮训练的loss值,可以没有
    for data in train_loader: # 遍历所有样本,train_loader是可迭代对象,保存了数据集中所有的数据
    imgs, targets = data # 将数据拆分成图片和标签
    outputs = module(imgs) # 将样本的输入值输入到自己的神经网络中
    loss = loss_func(outputs, targets, transform=True) # 计算loss / calculate loss
    trainning_loss += loss.value
    loss.backward() # 通过反向传播计算梯度 / calculate gradiant through back propagation
    optimizer.step() # 通过优化器调整模型参数 / adjust the weights of network through optimizer
    if i % TEST_STEP == 0: # 每训练TEST_STEP轮就测试一下当前训练的成果
    show_effect(i, module, loss_func, test_loader, i // TEST_STEP)
    print("{} turn finished, loss of train set = {}".format(i, trainning_loss))
  • 接下来逐个介绍编写的类,这些类在pytorch中都有同名同功能的类,是仿照pytorch来的:

  • Module

    • pytorch不同,只能有一个Sequential类(序列),在该类中定义好神经网络的各个层和顺序,然后传给Module类的构造函数
    • 正向传播:调用Sequential的正向传播
    • 反向传播:调用Sequential的反向传播
    • 目前为止,这个类的大部分功能与Sequential相同,只是套了个壳保证与pytorch相同
  • lossfunction

    • 有不同的loss函数,构造函数需要给他指定自己定义的神经网络的反向传播函数
    • 调用loss函数会返回一个Loss类的对象,该类记录了loss值。
    • 通过调用Loss类的.backward()方法就可以实现反向传播计算梯度
    • 内部机制:
      • 内部其实就是调用了自己定义的神经网络的反向传播函数
      • 也算是对于pytorch的一个拙劣模仿,完全没必要,直接通过Module调用就好
  • 优化器:

    • 目前只实现了随机梯度下降SGD
    • 构造函数的参数是自己定义的Module。在已经计算过梯度之后,调用optimizer.step()改变Module内各个层的参数值
    • 内部机制:
      • 目前由于只有SGD一种算法,所以暂时也只是一个拙劣模仿
      • 就是调用了一下Module.step(),再让Module调用Sequential.step(),最后由Sequential调用内部各个层的Layer.step()实现更新
      • 梯度值在loss.backward的时候计算、保存在各个层中了
  • Layer

    • 有许多不同的层

    • 共性

      • 前向传播

        • 接受一个输入进行前向传播计算,输出一个输出
        • 会将输入保存起来,在反向传播中要用
      • 反向传播
        • 接受前向传播的输出的梯度值,计算自身参数(如Linear中的w和b)的梯度值并保存起来
        • 输出值为前向传播的输入的梯度值,用来让上一层(可能没有)继续进行反向传播计算
        • 这样不同的层之间就可以进行任意的拼装而不妨碍前向传播、反向传播的进行了
      • .step方法
        • 更新自身的参数值(也可能没有,如激活层、池化层)
    • Sequential

      • 这个类也是继承自Layer,可以当作一层来使用

      • 它把多个层按照顺序拼装到一起,在前向、反向传播时按照顺序进行计算

      • 结合它的forwardbackward方法来理解:

        	def forward(self, x):
        out = x
        for layer in self.layers:
        out = layer(out)
        return out def backward(self, output_gradiant):
        layer_num = len(self.layers)
        delta = output_gradiant
        for i in range(layer_num - 1, -1, -1):
        # 反向遍历各个层, 将期望改变量反向传播
        delta = self.layers[i].backward(delta) def step(self, lr):
        for layer in self.layers:
        layer.step(lr)
    • RNN类:循环神经网络层

      • 继承自Layer,由于内容比较复杂故单独说明一下

      • RNN内部由一个全连接层Linear和一个激活层组成

      • 前向传播

        	    def forward(self, inputs):
        """
        :param inputs: input = (h0, x) h0.shape == (batch, out_dim) x.shape == (seq, batch, in_dim)
        :return: outputs: outputs.shape == (seq, batch, out_dim)
        """
        h = inputs[0] # 输入的inputs由两部分组成
        X = inputs[1]
        if X.shape[2] != self.in_dim or h.shape[1] != self.out_dim:
        # 检查输入的形状是否有问题
        raise ShapeNotMatchException(self, "forward: wrong shape: h0 = {}, X = {}".format(h.shape, X.shape)) self.seq_len = X.shape[0] # 时间序列的长度
        self.inputs = X # 保存输入,之后的反向传播还要用
        output_list = [] # 保存每个时间点的输出
        for x in X:
        # 按时间序列遍历input
        # x.shape == (batch, in_dim), h.shape == (batch, out_dim)
        h = self.activation(self.linear(np.c_[h, x]))
        output_list.append(h)
        self.outputs = np.stack(output_list, axis=0) # 将列表转换成一个矩阵保存起来
        return self.outputs
      • 反向传播

        	def backward(self, output_gradiant):
        """
        :param output_gradiant: shape == (seq, batch, out_dim)
        :return: input_gradiant
        """
        if output_gradiant.shape != self.outputs.shape:
        # 期望得到(seq, batch, out_dim)形状
        raise ShapeNotMatchException(self, "__backward: expected {}, but we got "
        "{}".format(self.outputs.shape, output_gradiant.shape)) input_gradients = []
        # 每个time_step上的虚拟weight_gradient, 最后求平均值就是总的weight_gradient
        weight_gradients = np.zeros(self.linear.weights_shape())
        bias_gradients = np.zeros(self.linear.bias_shape())
        batch_size = output_gradiant.shape[1] # total_gradient: 前向传播的时候是将x, h合成为一个矩阵,所以反向传播也先计算这个大矩阵的梯度再拆分为x_grad, h_grad
        total_gradient = np.zeros((batch_size, self.out_dim + self.in_dim))
        h_gradient = None # 反向遍历各个时间层,计算该层的梯度值
        for i in range(self.seq_len - 1, -1, -1):
        # 前向传播顺序: x, h -> z -> h
        # 所以反向传播计算顺序:h_grad -> z_grad -> x_grad, h_grad, w_grad, b_grad # %%%%%%%%%%%%%%计算平均值的版本%%%%%%%%%%%%%%%%%%%%%%%
        # h_gradient = (output_gradiant[i] + total_gradient[:, 0:self.out_dim]) / 2
        # %%%%%%%%%%%%%%不计算平均值的版本%%%%%%%%%%%%%%%%%%%%%%%
        # 计算h_grad: 这一时间点的h_grad包括输出的grad和之前的时间点计算所得grad两部分
        h_gradient = output_gradiant[i] + total_gradient[:, 0:self.out_dim] # w_grad和b_grad是在linear.backward()内计算的,不用手动再计算了
        z_gradient = self.activation.backward(h_gradient) # 计算z_grad
        total_gradient = self.linear.backward(z_gradient) # 计算x_grad和h_grad合成的大矩阵的梯度 # total_gradient 同时包含了h和x的gradient, shape == (batch, out_dim + in_dim)
        x_gradient = total_gradient[:, self.out_dim:] input_gradients.append(x_gradient)
        weight_gradients += self.linear.gradients["w"]
        bias_gradients += self.linear.gradients["b"] # %%%%%%%%%%%%%%%%%%计算平均值的版本%%%%%%%%%%%%%%%%%%%%%%%
        # self.linear.set_gradients(w=weight_gradients / self.seq_len, b=bias_gradients / self.seq_len)
        # %%%%%%%%%%%%%%%%%%不计算平均值的版本%%%%%%%%%%%%%%%%%%%%%%%
        self.linear.set_gradients(w=weight_gradients, b=bias_gradients) # 设置梯度值 list.reverse(input_gradients) # input_gradients是逆序的,最后输出时需要reverse一下
        print("sum(weight_gradients) = {}".format(np.sum(weight_gradients))) # np.stack的作用是将列表转变成一个矩阵
        return np.stack(input_gradients), h_gradient

神经网络:numpy实现神经网络框架的更多相关文章

  1. 基于Numpy的神经网络+手写数字识别

    基于Numpy的神经网络+手写数字识别 本文代码来自Tariq Rashid所著<Python神经网络编程> 代码分为三个部分,框架如下所示: # neural network class ...

  2. 针对深度学习(神经网络)的AI框架调研

    针对深度学习(神经网络)的AI框架调研 在我们的AI安全引擎中未来会使用深度学习(神经网络),后续将引入AI芯片,因此重点看了下业界AI芯片厂商和对应芯片的AI框架,包括Intel(MKL CPU). ...

  3. 神经网络_线性神经网络 2 (Nerual Network_Linear Nerual Network 2)

    1 LMS 学习规则 1.1 LMS学习规则定义 MSE=(1/Q)*Σe2k=(1/Q)*Σ(tk-ak)2,k=1,2,...,Q 式中:Q是训练样本:t(k)是神经元的期望输出:a(k)是神经元 ...

  4. RBF神经网络和BP神经网络的关系

    作者:李瞬生链接:https://www.zhihu.com/question/44328472/answer/128973724来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  5. 神经网络与BP神经网络

    一.神经元 神经元模型是一个包含输入,输出与计算功能的模型.(多个输入对应一个输出) 一个神经网络的训练算法就是让权重(通常用w表示)的值调整到最佳,以使得整个网络的预测效果最好. 事实上,在神经网络 ...

  6. 深度学习原理与框架-神经网络-线性回归与神经网络的效果对比 1.np.c_[将数据进行合并] 2.np.linspace(将数据拆成n等分) 3.np.meshgrid(将一维数据表示为二维的维度) 4.plt.contourf(画出等高线图,画算法边界)

    1. np.c[a, b]  将列表或者数据进行合并,我们也可以使用np.concatenate 参数说明:a和b表示输入的列表数据 2.np.linspace(0, 1, N) # 将0和1之间的数 ...

  7. 深度学习原理与框架-神经网络架构 1.神经网络构架 2.激活函数(sigmoid和relu) 3.图片预处理(减去均值和除标准差) 4.dropout(防止过拟合操作)

    神经网络构架:主要时表示神经网络的组成,即中间隐藏层的结构 对图片进行说明:我们可以看出图中的层数分布: input layer表示输入层,维度(N_num, input_dim)  N_num表示输 ...

  8. 吴裕雄 python 神经网络——TensorFlow 输入数据处理框架

    import tensorflow as tf files = tf.train.match_filenames_once("E:\\MNIST_data\\output.tfrecords ...

  9. 深度学习原理与框架-递归神经网络-RNN网络基本框架(代码?) 1.rnn.LSTMCell(生成单层LSTM) 2.rnn.DropoutWrapper(对rnn进行dropout操作) 3.tf.contrib.rnn.MultiRNNCell(堆叠多层LSTM) 4.mlstm_cell.zero_state(state初始化) 5.mlstm_cell(进行LSTM求解)

    问题:LSTM的输出值output和state是否是一样的 1. rnn.LSTMCell(num_hidden, reuse=tf.get_variable_scope().reuse)  # 构建 ...

随机推荐

  1. [心得体会]RabbitMQ

    RabbitMQ是什么? 消息队列, 基于AMQP(高级消息队列), 使用Erlang语言编写, 收发消息使用 有什么用? 有什么应用场景? 1. 任务异步处理 2. 应用程序解耦 为什么使用Rabb ...

  2. jenkins send files or publish

    1.创建一个自由风格项目 2.添加用户凭据 3.配置git 4.配置构建方式 这里选择 send files or execute command over SSH 5.配置远程发布脚本 6.构建 7 ...

  3. MySQL | MySQL5.7.* 安装

    清理系统环境 清理系统环境,保证安装时没有打扰. # 查看系统是否自带 mariadb-lib rpm -qa | grep mariadb # 如果有,输出:mariadb-libs-5.5.44- ...

  4. 测试管理工具 - Tuleap部署和安装使用教程

    安装 通过CentOS的安装,非常简单,命令直接为pip install tuleap 部署 登录管理员权限 登录名为中文名拼音,如wuweiping. 设置的默认密码为12345678,也可以进入配 ...

  5. C语言:float表示范围

    #include <stdio.h> #include <limits.h> //整数限制 #include <float.h> //浮点数限制 void main ...

  6. ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: NO)解决办法

    问题:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) 很久没用这台电脑的mysql ...

  7. [刘阳Java]_Spring常用注解介绍_第6讲

    Spring的注解是在Spring2.5的版本中引入的,目的简化XML配置.在企业开发过程中使用注解的频率非常高,但是学习注解的前提是大家一定要对Spring基于XML配置要熟悉,这是我个人建议,因为 ...

  8. Java安全之XStream 漏洞分析

    Java安全之XStream 漏洞分析 0x00 前言 好久没写漏洞分析文章了,最近感觉在审代码的时候,XStream 组件出现的频率比较高,借此来学习一波XStream的漏洞分析. 0x01 XSt ...

  9. ES6 箭头函数及this

    ES6 箭头函数及this 1.箭头函数 <script type="text/javascript"> //以前定义函数 let fun=function () { ...

  10. C标准库学习

    前言 C标准库源码可通过下列两个网站进行查看:The GNU C Library.Welcome to uClibc-ng! - Embedded C library 以下学习记录也是以这两个网站提供 ...