大神,能不能像图中那样,把程序是由一系列函数组成的整合到APP里,实现输入黄框数字

在前面的中提到函数这篇博客會分享我学习JavaScript中函数的详细内容。

函数就是一段具有一定功能的代码块

使用function关键字进行声明,函数的结构:

函数体 --->有很多的语句组成

函數调用是使用函数名称去调用需要注意的是:

函数名不加()只表示一段代码(静态)

匿名函数是没有名称的函数,执行方法:

参数是内部需要使用到的变量参数分为:

  • 形式函数:在定义函数的时候 fn(参数名称);
  • 实际参数:在函数调用的时候 传入实际的值(实际参数)。

注意: 实际参數和形式参数要一一对应在调用函数的时候 需要根据形式参数的要求传递
传参的类型:可以是function 也可以是object,基本数据类型

函数的返回值:return關键字

具体使用return关键字是根据函数的功能,比如做一个两个参数相加功能,那么函数就要返回a+b的结果

  1. 函数声明的时候 ,函数的名字要 根据變量的命名规则来就可以了见词达意;
  2. 如何去确定参数?根据函数的具体功能来确定的;
  3. 是否需要返回值到底返回值是什么?根据函數功能来如果外部需要用到这个函数的执行结果,你就需要return回去

前面发布的文章介绍了感知机叻解了感知机可以通过叠加层表示复杂的函数。遗憾的是设定合适的、能符合预期的输入与输出的权重,是由人工进行的从本章开始,将进入神经网络的学习首先介绍激活函数,因为它是连接感知机和神经网络的桥梁如果读者认知阅读了本专题知识,相信你必有收獲

感知机数学表达式的简化

前面我们介绍了用感知机接收两个输入信号的数学表示如下:

现在将上式改成更加简洁的形式,我们不妨想┅下上面的表达式(或者说是函数)输出y只有两种值0和1。因此我们引入一个新函数将上式改写成下式:

式(2)中,输入信号的总和会被函数h(x)转換转换后的值就是输出y。而式(3)所表示的h(x)在输入超过0时返回1否则返回0。不难理解式(1)和式(2)、(3)做的是一件事情。其实这和数学中的复合函數的意思一样(将b+w1x1+w2x2整体作为变量x变量名可随便取)。

 我们刚引入的h(x)函数能将输入信号的总和转换为输出信号(只有0和1)这种函数就是激活函数。正如“激活”二字激活函数就是决定如何来激活输入信号的总和。读者意识可能有点模糊我们对上面的式子再分析一下,将式(2)分为兩个阶段如下式,第一阶段是计算输入信号的加权和第二阶段则用激活函数来转换这一加权和。

式(4)计算输入信号和偏置b的总和记为a,接着用h()函数将a转换为输出y这里我们用下图来形象地表示式(4)和式(5)。

    0表示神经元这里我们新增了一个输入信号为常数1的神经元(灰色),其權重为b,主要是把偏置b(控制神经元被激活的容易程度)添加至感知机中把上图和上面的公式结合起来分析,节点a接收了输入信号的加权和接着节点a被激活函数h()转换为节点y输出。注意节点和神经元是一样的意思

试想什么样的函数的输出只有0和1两种值,毫无疑问阶跃函数(0处突变为1)的输出就是这样的。这里科普一下一般而言,单层感知机也称为朴素感知机即使用了阶跃函数作为激活函数的单层网络。哆层感知机指神经网络即使用了sigmoid函数等平滑函数作为激活函数的多层网络。

式(3)表明输入一旦超过阀值,激活函数就切换输出因此在感知机中的激活函数是“阶跃函数”。我们不难发问如果感知机使用其他函数作为激活函数,结果会怎么样呢答案是使用其他激活函數,那我们就进入了神经网络的世界了下面我们详细介绍在神经网络中常用的几种激活函数。

 


在神经网络中sigmoid函数常作为激活函数,其表达式如下:

式(6)中exp(-x)表示e-x次方的意思实际上,感知机和神经网络的主要区别就在于激活函数的不同Sigmoid函数作为激活函数,对信号进行转换转换后的信号被传送给下一个神经元。Python实现如下:
"""sigmoid函数除了函数不一样,绘图代码同上"""
 









#阶跃函数和sigmoid函数比较
 






(1)sigmoid函数是一条平滑的曲线即输出随输入发生连续的变化,阶跃函数以0为界发生急剧性的变化。sigmoid函数的平滑性对神经网络的学习极有帮助








(1)形状相似,输入小时輸出接近0;输入增大时,输出向1靠近也就是说,当输入信号为重要信息时阶跃函数和sigmoid函数都会输出较大的值;当输入信号为不重要的信息时,两者都输出较小的值





(3)两者都是非线性函数。阶跃函数是一条折线sigmoid函数是一条曲线。实际上神经网络的激活函数必须使用非線性函数,否则加深层的意义就没有了对于线性函数而言,不管如何加深层总存在与之等效的无隐藏层的神经网络。举个例子激活函数h(x)=cx,把y(x)=h(h(x))的运算对应2层神经网络显然y(x)=c·c·x=c2·x=ax(a= c2)。所以为了利用多层神经网络的优势激活函数必须使用非线性函数。











式(7)表明输入大于0,則直接输出该值;输入小于等于0时输出0。Python实现也很简单代码如下:


# 只给出函数,绘图代码同上
 



本章剩余部分的内容一直采用sigmoid函数作为噭活函数在靠后的知识点中,才使用ReLU函数


今天的内容就讲到这里了,希望读者好好回顾一下激活函数的产生及特点下一篇知识点,將介绍使用numpy数组实现神经网络欢迎读者订阅我的微信公众号“Python生态智联”,充分利用好零碎时间学AI

什么是 Google Protocol Buffer 假如您在网上搜索,应該会得到类似这样的文字介绍:

Protocol Buffers 是一种轻便高效的结构化数据存储格式可以用于结构化数据串行化,或者说序列化它很适合做数据存儲或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式目前提供了 C++、Java、Python 三种语言嘚 API。

或许您和我一样在第一次看完这些介绍后还是不明白 Protobuf 究竟是什么,那么我想一个简单的例子应该比较有助于理解它

一般情况下,使用 Protobuf 的人们都会先写好 .proto 文件再用 Protobuf 编译器生成目标语言所需要的源代码文件。将这些生成的代码和应用程序一起编译

可是在某且情况下,人们无法预先知道 .proto 文件他们需要动态处理一些未知的 .proto 文件。比如一个通用的消息转发中间件它不可能预知需要处理怎样的消息。这需要动态编译 .proto 文件并使用其中的 Message。

下面还是通过实例说明这些类的关系和使用吧

对于给定的 proto 文件,比如 lm.helloworld.proto在程序中动态编译它只需要佷少的一些代码。如代码清单 6 所示

首先构造一个 importer 对象。构造函数需要两个入口参数一个是 source Tree 对象,该对象指定了存放 .proto 文件的源目录第②个参数是一个 error collector 对象,该对象有一个 AddError 方法用来处理解析 .proto 文件时遇到的语法错误。

之后需要动态编译一个 .proto 文件时,只需调用 importer 对象的 import 方法非常简单。

那么我们如何使用动态编译后的 Message 呢我们需要首先了解几个其他的类

类 CommandLineInterface 封装了 protoc 编译器的前端,包括命令行参数的解析proto 文件嘚编译等功能。您所需要做的是实现类 CodeGenerator 的派生类实现诸如代码生成等后端工作:

程序的大体框架如图所示:

这样生成的编译器和 protoc 的使用方法相同,接受同样的命令行参数cli 将对用户输入的 .proto 进行词法语法等分析工作,最终生成一个语法树该树的结构如图所示。

其根节点为┅个 FileDescriptor 对象(请参考“动态编译”一节)并作为输入参数被传入 yourG 的 Generator() 方法。在这个方法内您可以遍历语法树,然后生成对应的您所需要的玳码简单说来,要想实现一个新的 compiler您只需要写一个 main 函数,和一个实现了方法 Generator() 的派生类即可

在本文的下载附件中,有一个参考例子將 .proto 文件编译生成 XML 的 compiler,可以作为参考

人们一直在强调,同 XML 相比 Protobuf 的主要优点在于性能高。它以高效的二进制方式存储比 XML 小 3 到 10 倍,快 20 到 100 倍

对于这些 “小 3 到 10 倍”,“快 20 到 100 倍”的说法,严肃的程序员需要一个解释因此在本文的最后,让我们稍微深入 Protobuf 的内部实现吧

有两项技术保证了采用 Protobuf 的程序能获得相对于 XML 极大的性能提高。

第一点我们可以考察 Protobuf 序列化后的信息内容。您可以看到 Protocol Buffer 信息的表示非常紧凑这意味著消息的体积减少,自然需要更少的资源比如网络上传输的字节数更少,需要的 IO 更少等从而提高性能。

第二点我们需要理解 Protobuf 封解包的夶致过程从而理解为什么会比 XML 快很多。

Protobuf 序列化后所生成的二进制消息非常紧凑这得益于 Protobuf 采用的非常巧妙的 Encoding 方法。

考察消息结构之前讓我首先要介绍一个叫做 Varint 的术语。

Varint 是一种紧凑的表示数字的方法它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数这能减少用来表示数字的字节数。

比如对于 int32 类型的数字一般需要 4 个 byte 来表示。但是采用 Varint对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示當然凡事都有好的也有不好的一面,采用 Varint 表示法大的数字则需要 5 个 byte 来表示。从统计的角度来说一般不会所有的消息中的数字都是大数,因此大多数情况下采用 Varint 后,可以用更少的字节数来表示数字信息下面就详细介绍一下 Varint。

Varint 中的每个 byte 的最高位 bit 有特殊的含义如果该位為 1,表示后续的 byte 也是该数字的一部分如果该位为 0,则结束其他的 7 个 bit 都用来表示数字。因此小于 128 的数字都可以用一个 byte 表示大于 128 的数字,比如 300会用两个字节来表示:00 0010

消息经过序列化后会成为一个二进制数据流,该流中的数据为一系列的 Key-Value 对如下图所示:

采用这种 Key-Pair 结构无需使用分隔符来分割不同的 Field。对于可选的 Field如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field这些特性都有助于节约消息本身的大小。

以玳码清单 1 中的消息为例假设我们生成如下的一个消息 Test1:

Wire Type 可能的类型如下表所示:

0

在计算机内,一个负数一般会被表示为一个很大的整数洇为计算机定义负数的符号位为数字的最高位。如果采用 Varint 表示一个负数那么一定需要 5 个 byte。为此 Google Protocol Buffer 定义了 sint32 这种类型采用 zigzag 编码。

Zigzag 编码用无符號数来表示有符号数字正数和负数交错,这就是 zigzag 这个词的含义了

使用 zigzag 编码,绝对值小的数字无论正负都可以采用较少的 byte 来表示,充汾利用了 Varint 这种技术

其他的数据类型,比如字符串等则采用类似数据库中的 varchar 的表示方法即用一个 varint 表示长度,然后将其余部分紧跟在这个長度部分之后即可

通过以上对 protobuf Encoding 方法的介绍,想必您也已经发现 protobuf 消息的内容小适于网络传输。假如您对那些有关技术细节的描述缺乏耐惢和兴趣那么下面这个简单而直观的比较应该能给您更加深刻的印象。

对于代码清单 1 中的消息用 Protobuf 序列化后的字节序列为:

而如果用 XML,則类似这样:

一共 55 个字节这些奇怪的数字需要稍微解释一下,其含义用 ASCII 表示如下:

首先我们来了解一下 XML 的封解包过程XML 需要从文件中读取出字符串,再转换为 XML 文档对象结构模型之后,再从 XML 文档对象结构模型中读取指定节点的字符串最后再将这个字符串转换成指定类型嘚变量。这个过程非常复杂其中将 XML 文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。

反观 Protobuf它只需要简单地将一个二进制序列,按照指定的格式读取到 C++ 对应的结构类型中就可以了从上一节的描述可以看到消息的 decoding 过程也可以通过几个位移操作组成的表达式计算即可完成。速度非常快

为了说明这并不是我拍脑袋随意想出来的说法,下面让我们简单分析一下 Protobuf 解包的代码鋶程吧

以代码清单 3 中的 Reader 为例,该程序首先调用 msg1 的 ParseFromIstream 方法这个方法解析从文件读入的二进制数据流,并将解析出来的数据赋予 helloworld 类的相应数據成员

该过程可以用下图表示:

的解码可以通过几个简单的数学运算完成,无需复杂的词法语法分析因此 ReadTag() 等方法都非常快。 在这个调鼡路径上的其他类和方法都非常简单感兴趣的读者可以自行阅读。 相对于 XML 的解析过程以上的流程图实在是非常简单吧?这也就是 Protobuf 效率高的第二个原因了

往往了解越多,人们就会越觉得自己无知我惶恐地发现自己竟然写了一篇关于序列化的文章,文中必然有许多想当嘫而自以为是的东西还希望各位能够去伪存真,更希望真的高手能不吝赐教给我来信。谢谢

我要回帖

更多关于 程序是由一系列函数组成的 的文章

 

随机推荐