(Python)零起步数学+神经网络入门

在这篇文章中,我们将在Python中从头开始了解用于构建具有各种层神经网络(完全连接,卷积等)的小型库中的机器学习和代码。最终,我们将能够写出如下内容:

假设你对神经网络已经有一定的了解,这篇文章的目的不是解释为什么构建这些模型,而是要说明如何正确实现

逐层

我们这里需要牢记整个框架:

1.     将数据输入神经网络

2.     在得出输出之前,数据从一层流向下一层

3.     一旦得到输出,就可以计算出一个标量误差

4.     最后,可以通过相对于参数本身减去误差的导数来调整给定参数(权重或偏差)。

5.     遍历整个过程。

最重要的一步是第四步。 我们希望能够拥有任意数量的层,以及任何类型的层。 但是如果修改/添加/删除网络中的一个层,网络的输出将会改变,误差也将改变,误差相对于参数的导数也将改变。无论网络架构如何、激活函数如何、损失如何,都必须要能够计算导数。

为了实现这一点,我们必须分别实现每一层

每个层应该实现什么

 

我们可能构建的每一层(完全连接,卷积,最大化,丢失等)至少有两个共同点:输入输出数据。

现在重要的一部分

 

假设给出一个层相对于其输出(E/Y)误差的导数,那么它必须能够提供相对于其输入(E/X)误差的导数

è®°ä½ï¼Eæ¯æ éï¼ä¸ä¸ªæ°å­ï¼ï¼XåYæ¯ç©éµã  

  

我们可以使用链规则轻松计算∂E/∂X的元素:

为什么是E/X

对于每一层,我们需要相对于其输入的误差导数,因为它将是相对于前一层输出的误差导数。这非常重要,这是理解反向传播的关键!在这之后,我们将能够立即从头开始编写深度卷积神经网络!

花样图解

 

基本上,对于前向传播,我们将输入数据提供给第一层,然后每层的输出成为下一层的输入,直到到达网络的末端。

对于反向传播,我们只是简单使用链规则来获得需要的导数。这就是为什么每一层必须提供其输出相对于其输入的导数。

这可能看起来很抽象,但是当我们将其应用于特定类型的层时,它将变得非常清楚。现在是编写第一个python类的好时机。

抽象基类:Layer

所有其它层将继承的抽象类Layer会处理简单属性,这些属性是输入输出以及前向反向方法。

from abc import abstractmethod
# Base class
class Layer:def __init__(self):self.input = None;self.output = None;self.input_shape = None;self.output_shape = None;# computes the output Y of a layer for a given input X@abstractmethoddef forward_propagation(self, input):raise NotImplementedError# computes dE/dX for a given dE/dY (and update parameters if any)@abstractmethoddef backward_propagation(self, output_error, learning_rate):raise NotImplementedError

正如你所看到的,在back_propagation函数中,有一个我没有提到的参数,它是learning_rate。 此参数应该类似于更新策略或者在Keras中调用它的优化器,为了简单起见,我们只是通过学习率并使用梯度下降更新我们的参数。

全连接层

现在先定义并实现第一种类型的网络层:全连接层或FC层。FC层是最基本的网络层,因为每个输入神经元都连接到每个输出神经元。

前向传播

每个输出神经元的值由下式计算:

使用矩阵,可以使用点积来计算每一个输出神经元的值:

当完成前向传播之后,现在开始做反向传播。

反向传播

正如我们所说,假设我们有一个矩阵,其中包含与该层输出相关的误差导数(∂E/∂Y)。 我们需要 :

1.关于参数的误差导数(∂E/∂W,∂E/∂B)

2.关于输入的误差导数(∂E/∂X)

首先计算∂E/∂W,该矩阵应与W本身的大小相同:对于ixj,其中i是输入神经元的数量,j是输出神经元的数量。每个权重都需要一个梯度

使用前面提到的链规则,可以写出:

那么:

这就是更新权重的第一个公式!现在开始计算∂E/∂B:

同样,∂E/∂B需要与B本身具有相同的大小,每个偏差一个梯度。 我们可以再次使用链规则:

得出结论:

现在已经得到E/WE/B,我们留下E/X这是非常重要的,因为它将“作用”为之前层的∂E/∂Y。

再次使用链规则:

最后,我们可以写出整个矩阵:

æ们已ç»å¾å°FCå±æéçä¸ä¸ªå¬å¼ï¼ 

   

编码全连接层

现在我们可以用Python编写实现:

from layer import Layer
import numpy as np# inherit from base class Layer
class FCLayer(Layer):# input_shape = (1,i)   i the number of input neurons# output_shape = (1,j)  j the number of output neuronsdef __init__(self, input_shape, output_shape):self.input_shape = input_shape;self.output_shape = output_shape;self.weights = np.random.rand(input_shape[1], output_shape[1]) - 0.5;self.bias = np.random.rand(1, output_shape[1]) - 0.5;# returns output for a given inputdef forward_propagation(self, input):self.input = input;self.output = np.dot(self.input, self.weights) + self.bias;return self.output;# computes dE/dW, dE/dB for a given output_error=dE/dY. Returns input_error=dE/dX.def backward_propagation(self, output_error, learning_rate):input_error = np.dot(output_error, self.weights.T);dWeights = np.dot(self.input.T, output_error);# dBias = output_error# update parametersself.weights -= learning_rate * dWeights;self.bias -= learning_rate * output_error;return input_error;

激活层

到目前为止所做的计算都完全是线性的。用这种模型学习是没有希望的,需要通过将非线性函数应用于某些层的输出来为模型添加非线性。

现在我们需要为这种新类型的层(激活层)重做整个过程!

不用担心,因为此时没有可学习的参数,过程会快点,只需要计算∂E/∂X。

我们将f和f'分别称为激活函数及其导数。

前向传播

正如将看到的,它非常简单。对于给定的输入X,输出是关于每个X元素的激活函数,这意味着输入输出具有相同的大小

反向传播

给出∂E/∂Y,需要计算∂E/∂X

注意,这里我们使用两个矩阵之间的每个元素乘法(而在上面的公式中,它是一个点积)

编码实现激活层

激活层的代码非常简单:

from layer import Layer
# inherit from base class Layer
class ActivationLayer(Layer):# input_shape = (1,i)   i the number of input neuronsdef __init__(self, input_shape, activation, activation_prime):self.input_shape = input_shape;self.output_shape = input_shape;self.activation = activation;self.activation_prime = activation_prime;# returns the activated inputdef forward_propagation(self, input):self.input = input;self.output = self.activation(self.input);return self.output;# Returns input_error=dE/dX for a given output_error=dE/dY.# learning_rate is not used because there is no "learnable" parameters.def backward_propagation(self, output_error, learning_rate):return self.activation_prime(self.input) * output_error;

可以在单独的文件中编写一些激活函数以及它们的导数,稍后将使用它们构建ActivationLayer:

import numpy as np
# activation function and its derivative
def tanh(x):return np.tanh(x);def tanh_prime(x):return 1-np.tanh(x)**2;

损失函数

到目前为止,对于给定的层,我们假设给出了∂E/∂Y(由下一层给出)。但是最后一层怎么得到∂E/∂Y?我们通过简单地手动给出最后一层的∂E/∂Y,它取决于我们如何定义误差。

网络的误差由自己定义,该误差衡量网络对给定输入数据的好坏程度。有许多方法可以定义误差,其中一种最常见的叫做MSE - Mean Squared Error:

其中y *和y分别表示期望的输出实际输出。你可以将损失视为最后一层,它将所有输出神经元吸收并将它们压成一个神经元。与其他每一层一样,需要定义∂E/∂Y。除了现在,我们终于得到E!

以下是两个python函数,可以将它们放在一个单独的文件中,将在构建网络时使用。

import numpy as np# loss function and its derivative
def mse(y_true, y_pred):return np.mean(np.power(y_true-y_pred, 2));def mse_prime(y_true, y_pred):return 2*(y_pred-y_true)/y_true.size;

网络类

到现在几乎完成了!我们将构建一个Network类来创建神经网络,非常容易,类似于第一张图片!

我注释了代码的每一部分,如果你掌握了前面的步骤,那么理解它应该不会太复杂。

from layer import Layerclass Network:def __init__(self):self.layers = [];self.loss = None;self.loss_prime = None;# add layer to networkdef add(self, layer):self.layers.append(layer);# set loss to usedef use(self, loss, loss_prime):self.loss = loss;self.loss_prime = loss_prime;# predict output for given inputdef predict(self, input):# sample dimension firstsamples = len(input);result = [];# run network over all samplesfor i in range(samples):# forward propagationoutput = input[i];for layer in self.layers:# output of layer l is input of layer l+1output = layer.forward_propagation(output);result.append(output);return result;# train the networkdef fit(self, x_train, y_train, epochs, learning_rate):# sample dimension firstsamples = len(x_train);# training loopfor i in range(epochs):err = 0;for j in range(samples):# forward propagationoutput = x_train[j];for layer in self.layers:output = layer.forward_propagation(output);# compute loss (for display purpose only)err += self.loss(y_train[j], output);# backward propagationerror = self.loss_prime(y_train[j], output);# loop from end of network to beginningfor layer in reversed(self.layers):# backpropagate dEerror = layer.backward_propagation(error, learning_rate);# calculate average error on all sampleserr /= samples;print('epoch %d/%d   error=%f' % (i+1,epochs,err));

构建一个神经网络

最后!我们可以使用我们的类来创建一个包含任意数量层的神经网络!为了简单起见,我将向你展示如何构建......一个XOR。

from network import Network
from fc_layer import FCLayer
from activation_layer import ActivationLayer
from losses import *
from activations import *
import numpy as np# training data
x_train = np.array([[[0,0]], [[0,1]], [[1,0]], [[1,1]]]);
y_train = np.array([[[0]], [[1]], [[1]], [[0]]]);# network
net = Network();
net.add(FCLayer((1,2), (1,3)));
net.add(ActivationLayer((1,3), tanh, tanh_prime));
net.add(FCLayer((1,3), (1,1)));
net.add(ActivationLayer((1,1), tanh, tanh_prime));# train
net.use(mse, mse_prime);
net.fit(x_train, y_train, epochs=1000, learning_rate=0.1);# test
out = net.predict(x_train);
print(out);

同样,我认为不需要强调很多事情,只需要仔细训练数据,应该能够先获得样本维度。例如,对于xor问题,样式应为(4,1,2)。

结果

$ python xor.py epoch 1/1000 error=0.322980 epoch 2/1000 error=0.311174 epoch 3/1000 error=0.307195 ... epoch 998/1000 error=0.000243 epoch 999/1000 error=0.000242 epoch 1000/1000 error=0.000242 [array([[ 0.00077435]]), array([[ 0.97760742]]), array([[ 0.97847793]]), array([[-0.00131305]])]

卷积层

这篇文章开始很长,所以我不会描述实现卷积层的所有步骤。但是,这是我做的一个实现:

from layer import Layer
from scipy import signal
import numpy as np# inherit from base class Layer
# This convolutional layer is always with stride 1
class ConvLayer(Layer):# input_shape = (i,j,d)# kernel_shape = (m,n)# layer_depth = output depthdef __init__(self, input_shape, kernel_shape, layer_depth):self.input_shape = input_shape;self.input_depth = input_shape[2];self.kernel_shape = kernel_shape;self.layer_depth = layer_depth;self.output_shape = (input_shape[0]-kernel_shape[0]+1, input_shape[1]-kernel_shape[1]+1, layer_depth);self.weights = np.random.rand(kernel_shape[0], kernel_shape[1], self.input_depth, layer_depth) - 0.5;self.bias = np.random.rand(layer_depth) - 0.5;# returns output for a given inputdef forward_propagation(self, input):self.input = input;self.output = np.zeros(self.output_shape);for k in range(self.layer_depth):for d in range(self.input_depth):self.output[:,:,k] += signal.correlate2d(self.input[:,:,d], self.weights[:,:,d,k], 'valid') + self.bias[k];return self.output;# computes dE/dW, dE/dB for a given output_error=dE/dY. Returns input_error=dE/dX.def backward_propagation(self, output_error, learning_rate):in_error = np.zeros(self.input_shape);dWeights = np.zeros((self.kernel_shape[0], self.kernel_shape[1], self.input_depth, self.layer_depth));dBias = np.zeros(self.layer_depth);for k in range(self.layer_depth):for d in range(self.input_depth):in_error[:,:,d] += signal.convolve2d(output_error[:,:,k], self.weights[:,:,d,k], 'full');dWeights[:,:,d,k] = signal.correlate2d(self.input[:,:,d], output_error[:,:,k], 'valid');dBias[k] = self.layer_depth * np.sum(output_error[:,:,k]);self.weights -= learning_rate*dWeights;self.bias -= learning_rate*dBias;return in_error;

它背后的数学实际上并不复杂!这是一篇很好的文章,你可以找到∂E/∂W,∂E/∂B和∂E/∂X的解释和计算。

如果你想验证你的理解是否正确,请尝试自己实现一些网络层,如MaxPooling,Flatten或Dropout

GitHub库

你可以在GitHub库中找到用于该文章的完整代码。

 

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

必须要看的网上冲浪安全攻略!

戳蓝字“CSDN云计算”关注我们哦!作者 | Nirnay Butle翻译|风车云马责编 | 阿秃出品 | CSDN云计算(ID:CSDNcloud)不管我们是不是技术迷,无可否认的是,现在我们各自的生活都对互联网产生了高度依…

当自己犹豫时,坐下来读一读

物来顺应:事情已经过去了,就不要纠结,应该顺应和面对 未来不迎:千万不要为了没有发生的事,而感到焦虑 当时不杂:专注于做好当前的事,不要三心二意 既过不恋:已经过去的事情&#…

java ajax查询_java-如何计时ajax查询(发送查询,处理,接收响应)

编辑以澄清我的意图:(基于初始答案)我有一个网络应用程序.该服务器由一组Java POJO组成,我正在使用Jersey将它们公开为REST API.浏览器使用jquery ajax调用这些API,并执行操作.我想记录我的Ajax查询所用的持续时间,并希望细分>将查询从浏览器发送到服务器花费了多…

短视频宝贝=慢?阿里巴巴工程师这样秒开短视频

前言 随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这个问题我们展开了一波优化&am…

AI又被彩虹吹?!新浪财经:应届博士算法毕业,80万年薪被疯抢

在2019人工智能计算大会上发布了《2019—2020中国人工智能计算力发展评估报告》。根据报告,预计2023年,中国人工智能基础架构市场将超过80亿美元。人工智能市场布局火力全开巨头哭诉:全球只有30万人才截至2017年,我国人工智能市场…

Haproxy 管控台介绍

Queue 队列 简称全称说明Curcurrent queued requests当前的队列请求数量Maxmax queued requests最大的队列请求数量Limit队列限制数量 Session rate (每秒的连接回话)列表 简称全称说明scurcurrent sessions每秒的当前回话的限制数量smaxmax sessions每秒的新的最大的回话量s…

阿里云时空数据库引擎HBase Ganos上线,场景、功能、优势全解析

随着全球卫星导航定位系统、传感网、移动互联网、IoT等技术的快速发展,越来越多的终端设备连接至网络,由此产生了大规模的时空位置信息,如车辆轨迹、个人轨迹、群体活动、可穿戴设备时空位置等。这些数据具有动态变化(数据写入频繁…

云栖专辑|阿里开发者们的第二个感悟:PG大V德哥的使命感与开放心态

2015年12月20日,云栖社区上线。2018年12月20日,云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

【经典必看】14个实用的数据库设计技巧

戳蓝字“CSDN云计算”关注我们哦!作者 | 程序IT圈责编 | 阿秃1. 原始单据与实体之间的关系可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一…

VS Code 全局配置

文章目录1. settings.json2. 在项目根目录添加.eslintrc.js3. 在项目根目录添加.prettierrc.json1. settings.json ctrlshirtp 搜索settings.json替换为下面内容即可 {// 主题颜色 浅色主题"workbench.colorTheme": "Monokai","workbench.iconTheme…

云栖专辑 | 阿里开发者们的第3个感悟:从身边开源开始学习,用过才能更好理解代码

2015年12月20日,云栖社区上线。2018年12月20日,云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

个人帐目管理系统java_Java 项目 个人帐目管理系统

目录第一部分项目描述 31.1项目目的 3第二部分需求和开发环境 32.1使用技术和开发环境 32.2项目需求 32.3详细功能 32.4 E-R图 32.5数据库的设计 32.5.1数据表的设计 32.5.2数据库约束的设计 42.5.3数据库序列的设计 42.5.4数据库索引的设计 42.5.5数据库视图的设计 52.5.6数据…

Visual Studio Code

文章目录一、基础配置1. 中文界面2. 高效插件3. vue代码片段4. 全局配置5. VSCode 常用快捷键6. VSCode高级操作技巧一、基础配置 1. 中文界面 中文界面 2. 高效插件 VSCode 高效必备开发插件 3. vue代码片段 Visual Studio Code Vue代码片段 4. 全局配置 Visual Studi…

KubeCon 2018 参会记录 —— FluentBit Deep Dive

在最近的上海和北美KubeCon大会上,来自于Treasure Data的Eduardo Silva(Fluentd Maintainer)带来了最期待的关于容器日志采集工具FluentBit的最新进展以及深入解析的分享;我们知道Fluentd是在2016年底正式加入CNCF,成为…

全球首个!阿里云开源批流一体机器学习平台Alink……

11月28日,阿里云正式开源机器学习平台 Alink,这也是全球首个批流一体的算法平台,旨在降低算法开发门槛,帮助开发者掌握机器学习的生命全周期。 Flink Forward 2019在京举办,吸引众多开发者参与标题Alink基于实时计算引…

2018年的AI/ML惊喜及预测19年的走势(一)

考虑到技术变革的速度,我认为让专业IT人士分享他们对2018年最大惊喜及2019年预测的看法会很有趣。以下是他们对人工智能(AI),机器学习( ML)和其他数据科学迭代的看法: CLARA分析公司首席执行官…

Java访问静态常量_Java如何在Spring EL中访问静态方法或常量?

在这个例子中,您将学习如何使用Spring Expression Language访问类范围的方法或常量。要访问类范围的方法或常量T(),例如,您将需要使用Spring EL的运算符T(java.lang.Math)。该运算符将使我们能够访问给定类上的静态方法和常量。作为示例&…

聚焦产业·城市、擎领数字未来:IMPACT2019紫光云峰会在津成功举办

近日,紫光云技术有限公司在天津举行主题为“产业城市 擎领未来”的IMPACT2019紫光云峰会,深度阐释打造产业数字引擎的理念和实践,并为unI X云创中心揭牌,发布紫光云芯片产业数字引擎。 天津市人民政府副秘书长杨明远为大会致辞会上…

IntelliJ IDEA 2020 创建xml文件

1、file—setting,左上角输入template, 2、在左侧栏找到File And Code Templates 3、中间选中Files 4、点击号,添加模板 5、输入模板名字:Name:mybatis-cfg.xml (name可以自定义) 6、后缀名extension&#…