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

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. 16 shell select in 循环

    select in 是 Shell 独有的一种循环,适用于与终端(Terminal)进行交互,在其他编程语言中是没有的. 用法 说明 脚本 select var in val_listdo    st ...

  2. 15 shell for循环

    除了 while 循环和 until 循环,Shell 脚本中还有for 循环,for 循环有两种使用形式:C语言风格的for循环与Python语言风格的for in循环,两种形式的for循环用法对比 ...

  3. K8S(Kubernetes)学习笔记

    Kubernetes(k8s)是google提供的开源的容器集群管理系统,在Docker技术的基础上,为容器化的应用提供部署运行.资源调度.服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理 ...

  4. MYSQL数据库数据拆分之分库分表总结 (转)

      数据存储演进思路一:单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 数据存储演进思路二:单库多表 随着用户数 ...

  5. Java实验项目三——面向对象定义职工类和日期类

    Program:按照如下要求设计类: (1)设计一个日期类,用于记录年.月.日,并提供对日期处理的常用方法. (2)设计一个职工类,该职工类至少具有下面的属性:职工号,姓名,性别,生日,工作部门,参加 ...

  6. 使用Hugo框架搭建博客的过程 - 页面模板

    前言 最初在制作友链界面时,没有学习Hugo框架,一头雾水.网上有关的教程甚少,只能去学一遍Hugo. 在学习Hugo的过程中,了解了列表模板,分类模板.开发了几个功能页面,如:留言板,友链,记忆分类 ...

  7. ESP32构建系统(CMake版)

    ESP32 芯片是一款 2.4 GHz Wi-Fi 和蓝牙双模芯片,内置 1 或 2 个 32 位处理器,运算能力最高可达 600 DMIPS. ESP-IDF 即乐鑫物联网开发框架,可为在 Wind ...

  8. C语言:逻辑运算符||

    #include <stdio.h> //逻辑运算符||特点:左右两边的表达式先做左边,如果左边为1则右边不用执行,整个结果为1:如果左边为0,再执行右边: main() { int x= ...

  9. Kubernetes部署-二进制方式

    环境配置 一.系统环境 序号 用途 系统 Docker版本 IP地址 1 Master CentOS Linux release 7.6.1810 (Core) 19.03.4 192.168.0.1 ...

  10. 开发工具IDE从入门到爱不释手(一)项目初始配置

    前言 版本:IDE 2019.2.3 JDK:1.8 一.字体 快捷键:Ctrl+Alt+S  ;打开Settings,一般系统配置都在这里 输入font,需要修改字体有三处 Apperance:ID ...