精华内容
下载资源
问答
  • 主要是开发了系统启动时的窗体淡入淡出效果,并且包括通过tabpanel来做的“上一步一步”的程序代码,适合通用程序的开发界面参考
  • IDEA中如何使用debug调试项目 一步一步详细教程

    万次阅读 多人点赞 2018-07-09 16:45:46
     一般情况下我们调试的时候是在一个线程中的,一步一步往下走。但有时候你会发现在Debug的时候,想发起另外一个请求都无法进行了?  那是因为IDEA在Debug时默认阻塞级别是ALL,会阻塞其它线程,只有在当前调试...

    原文链接:http://www.yxlzone.top/show_blog_details_by_id?id=2bf6fd4688e44a7eb560f8db233ef5f7

    在现在的开发中,我们经常采用Debug来追踪代码的运行流程,通常在程序运行过程中出现异常,启用Debug模式可以分析定位异常发生的位置,以及在运行过程中参数的变化。通常我们也可以启用Debug模式来跟踪代码的运行流程去学习三方框架的源码。

      所以学习下如何在Intellij IDEA中使用好Debug,主要包括如下内容:

        一、Debug开篇

        二、基本用法&快捷键

        三、变量查看

        四、计算表达式

        五、智能步入

        六、断点条件设置

        七、多线程调试

        八、回退断点

        九、中断Debug

        十、附:JRebel激活

     


    一、Debug开篇

      首先看下IDEA中Debug模式下的界面。

      如下是在IDEA中启动Debug模式,进入断点后的界面,我这里是Windows,可能和Mac的图标等会有些不一样。就简单说下图中标注的8个地方:

      ① 以Debug模式启动服务,左边的一个按钮则是以Run模式启动。在开发中,我一般会直接启动Debug模式,方便随时调试代码。

      ② 断点:在左边行号栏单击左键,或者快捷键Ctrl+F8 打上/取消断点,断点行的颜色可自己去设置。

      ③ Debug窗口:访问请求到达第一个断点后,会自动激活Debug窗口。如果没有自动激活,可以去设置里设置,如图1.2。

      ④ 调试按钮:一共有8个按钮,调试的主要功能就对应着这几个按钮,鼠标悬停在按钮上可以查看对应的快捷键。在菜单栏Run里可以找到同样的对应的功能,如图1.4。

      ⑤ 服务按钮:可以在这里关闭/启动服务,设置断点等。

      ⑥ 方法调用栈:这里显示了该线程调试所经过的所有方法,勾选右上角的[Show All Frames]按钮,就不会显示其它类库的方法了,否则这里会有一大堆的方法。

      ⑦ Variables:在变量区可以查看当前断点之前的当前方法内的变量。

      ⑧ Watches:查看变量,可以将Variables区中的变量拖到Watches中查看 

      [图1.1]

      

       [图1.2]:在设置里勾选Show debug window on breakpoint,则请求进入到断点后自动激活Debug窗口

      

      [图1.3]:如果你的IDEA底部没有显示工具栏或状态栏,可以在View里打开,显示出工具栏会方便我们使用。可以自己去尝试下这四个选项。

      

      [图1.4]:在菜单栏Run里有调试对应的功能,同时可以查看对应的快捷键。

      

     


    二、基本用法&快捷键

    Debug调试的功能主要对应着图一中4和5两组按钮:

      1、首先说第一组按钮,共8个按钮,从左到右依次如下:

         [图2.1]

        > Show Execution Point (Alt + F10):如果你的光标在其它行或其它页面,点击这个按钮可跳转到当前代码执行的行。

        > Step Over (F8):步过,一行一行地往下走,如果这一行上有方法不会进入方法。

        > Step Into (F7):步入,如果当前行有方法,可以进入方法内部,一般用于进入自定义方法内,不会进入官方类库的方法,如第25行的put方法。

        > Force Step Into (Alt + Shift + F7):强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。

        > Step Out (Shift + F8):步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。

        > Drop Frame (默认无):回退断点,后面章节详细说明。

        > Run to Cursor (Alt + F9):运行到光标处,你可以将光标定位到你需要查看的那一行,然后使用这个功能,代码会运行至光标行,而不需要打断点。

        > Evaluate Expression (Alt + F8):计算表达式,后面章节详细说明。

      2、第二组按钮,共7个按钮,从上到下依次如下:

          [图2.2]

        > Rerun 'xxxx':重新运行程序,会关闭服务后重新启动程序。

        > Update 'tech' application (Ctrl + F5):更新程序,一般在你的代码有改动后可执行这个功能。而这个功能对应的操作则是在服务配置里,如图2.3。

        > Resume Program (F9):恢复程序,比如,你在第20行和25行有两个断点,当前运行至第20行,按F9,则运行到下一个断点(即第25行),再按F9,则运行完整个流程,因为后面已经没有断点了。

        > Pause Program:暂停程序,启用Debug。目前没发现具体用法。

        > Stop 'xxx' (Ctrl + F2):连续按两下,关闭程序。有时候你会发现关闭服务再启动时,报端口被占用,这是因为没完全关闭服务的原因,你就需要查杀所有JVM进程了。

        > View Breakpoints (Ctrl + Shift + F8):查看所有断点,后面章节会涉及到。

        > Mute Breakpoints:哑的断点,选择这个后,所有断点变为灰色,断点失效,按F9则可以直接运行完程序。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled,如图2.4,则该行断点失效。

         [图2.3]:更新程序,On 'Update' actions,执行更新操作时所做的事情,一般选择'Update classes and resources',即更新类和资源文件。

           一般配合热部署插件会更好用,如JRebel,这样就不用每次更改代码后还要去重新启动服务。如何激活JRebel,在最后章节附上。

           下面的On frame deactivation,在IDEA窗口失去焦点时触发,即一般你从idea切换到浏览器的时候,idea会自动帮你做的事情,一般可以设置Do nothing,频繁切换会比较消耗资源的。

           

        [图2.4]

        

     


    三、变量查看

    在Debug过程中,跟踪查看变量的变化是非常必要的,这里就简单说下IDEA中可以查看变量的几个地方,相信大部分人都了解。

      1、如下,在IDEA中,参数所在行后面会显示当前变量的值。

       [图3.1]

      2、光标悬停到参数上,显示当前变量信息。点击打开详情如图3.3。我一般会使用这种方式,快捷方便。

       [图3.2]

       [图3.3]

      3、在Variables里查看,这里显示当前方法里的所有变量。

        [图3.4]

      4、在Watches里,点击New Watch,输入需要查看的变量。或者可以从Variables里拖到Watche里查看。

       [图3.5]

      如果你发现你没有Watches,可能在下图所在的地方。

       [图3.6]  

         [图3.7]

     


    四、计算表达式

      在前面提到的计算表达式如图4.1的按钮,Evaluate Expression (Alt + F8) 。可以使用这个操作在调试过程中计算某个表达式的值,而不用再去打印信息。

       [图4.1]

      1、按Alt + F8或按钮,或者,你可以选中某个表达式再Alt + F8,弹出计算表达式的窗口,如下,回车或点击Evaluate计算表达式的值。

         这个表达式不仅可以是一般变量或参数,也可以是方法,当你的一行代码中调用了几个方法时,就可以通过这种方式查看查看某个方法的返回值。

       [图4.2]

      2、设置变量,在计算表达式的框里,可以改变变量的值,这样有时候就能很方便我们去调试各种值的情况了不是。

       [图4.3]

     


    五、智能步入

      想想,一行代码里有好几个方法,怎么只选择某一个方法进入。之前提到过使用Step Into (Alt + F7) 或者 Force Step Into (Alt + Shift + F7)进入到方法内部,但这两个操作会根据方法调用顺序依次进入,这比较麻烦。

      那么智能步入就很方便了,智能步入,这个功能在Run里可以看到,Smart Step Into (Shift + F7),如图5.1

       [图5.1]

      按Shift + F7,会自动定位到当前断点行,并列出需要进入的方法,如图5.2,点击方法进入方法内部。

      如果只有一个方法,则直接进入,类似Force Step Into。

       [图5.2]

     


    六、断点条件设置

      通过设置断点条件,在满足条件时,才停在断点处,否则直接运行。

      通常,当我们在遍历一个比较大的集合或数组时,在循环内设置了一个断点,难道我们要一个一个去看变量的值?那肯定很累,说不定你还错过这个值得重新来一次。

      1、在断点上右键直接设置当前断点的条件,如图6.1,我设置exist为true时断点才生效。

        [图6.1]

      2、点击View Breakpoints (Ctrl + Shift + F8),查看所有断点。

        Java Line Breakpoints 显示了所有的断点,在右边勾选Condition,设置断点的条件。

        勾选Log message to console,则会将当前断点行输出到控制台,如图6.3

        勾选Evaluate and log,可以在执行这行代码是计算表达式的值,并将结果输出到控制台。

       [图6.2]

      

       [图6.3]

      

      3、再说说右边的Filters过滤,这些一般情况下不常用,简单说下意思。

        Instance filters:实例过滤,输入实例ID(如图6.5中的实例ID),但是我这里没有成功,不知道什么原因,知道的朋友留个言。

        Class filters:类过滤,根据类名过滤,同样没有成功....

        Pass count:用于循环中,如果断点在循环中,可以设置该值,循环多少次后停在断点处,之后的循环都会停在断点处。

      [图6.4]

       

       [图6.5]

      

      4、异常断点,通过设置异常断点,在程序中出现需要拦截的异常时,会自动定位到异常行。

      如图6.6,点击+号添加Java Exception Breakpoints,添加异常断点。然后输入需要断点的异常类,如图6.7,之后可以在Java Exception Breakpoints里看到添加的异常断点。

      我这里添加了一个NullPointerException异常断点,如图6.8,出现空指针异常后,自动定位在空指针异常行。

       [图6.6]

      

      [图6.7]

       

      [图6.8]

       

     


    七、多线程调试

      一般情况下我们调试的时候是在一个线程中的,一步一步往下走。但有时候你会发现在Debug的时候,想发起另外一个请求都无法进行了?

      那是因为IDEA在Debug时默认阻塞级别是ALL,会阻塞其它线程,只有在当前调试线程走完时才会走其它线程。可以在View Breakpoints里选择Thread,如图7.1,然后点击Make Default设置为默认选项。

      [图7.1]

       

      切换线程,在图7.2中Frames的下拉列表里,可以切换当前的线程,如下我这里有两个Debug的线程,切换另外一个则进入另一个Debug的线程。

      [图7.2]

       

     


    八、回退断点

      在调试的时候,想要重新走一下流程而不用再次发起一个请求?

      1、首先认识下这个方法调用栈,如图8.1,首先请求进入DemoController的insertDemo方法,然后调用insert方法,其它的invoke我们且先不管,最上面的方法是当前断点所在的方法。

      [图8.1]

       

      2、断点回退

      所谓的断点回退,其实就是回退到上一个方法调用的开始处,在IDEA里测试无法一行一行地回退或回到到上一个断点处,而是回到上一个方法。

      回退的方式有两种,一种是Drop Frame按钮(图8.2),按调用的方法逐步回退,包括三方类库的其它方法(取消Show All Frames按钮会显示三方类库的方法,如图8.3)。

      第二种方式,在调用栈方法上选择要回退的方法,右键选择Drop Frame(图8.4),回退到该方法的上一个方法调用处,此时再按F9(Resume Program),可以看到程序进入到该方法的断点处了。

      但有一点需要注意,断点回退只能重新走一下流程,之前的某些参数/数据的状态已经改变了的是无法回退到之前的状态的,如对象、集合、更新了数据库数据等等。

      图[8.2]

      

      图[8.3]

      

      图[8.4]

      

     


    九、中断Debug

      想要在Debug的时候,中断请求,不要再走剩余的流程了?

      有些时候,我们看到传入的参数有误后,不想走后面的流程了,怎么中断这次请求呢(后面的流程要删除数据库数据呢....),难道要关闭服务重新启动程序?嗯,我以前也是这么干的。

      确切的说,我也没发现可以直接中断请求的方式(除了关闭服务),但可以通过Force Return,即强制返回来避免后续的流程,如图9.1。

      点击Force Return,弹出Return Value的窗口,我这个方法的返回类型为Map,所以,我这里直接返回 results,来强制返回,从而不再进行后续的流程。或者你可以new HashMap<>()。

      [图9.1]

      

      [图9.2]

      

     


    十、附:JRebel激活

      目前本人一直使用JRebel做热部署工具,效果还算理想,修改Java代码或者xml等配置文件都能热更新。偶尔服务开久了,或更改的文件较多时,热更新没有生效,需要重新启动服务。

      这里只是简单说下我在网上看到的一种免费获得永久使用权的方式(非破解),不确定这种方式什么时候不能用。

      ① 首先你需要一个Facebook或Twitter的账号(最好Twitter)

      ② 进入这个网址:https://my.jrebel.com/,并登录,如图10.1

      ③ 然后在Install and Acticate里可以得到你的永久激活码。

      ④ 在设置里Jrebel里设置激活码,如图10.3,如果没有安装JRebel插件,先在Plugins里安装搜索安装JRebel插件。

      [图10.1]

       

      [图10.2]

      

      [图10.3]

      

    本文是作者转载用来备忘的!

    原文请看:http://www.yxlzone.top/show_blog_details_by_id?id=2bf6fd4688e44a7eb560f8db233ef5f7

     

     

    展开全文
  • 在本文中,我将向大家介绍Web服务的一些基本知识以及如何用VisualC#一步一步地创建一个简单的Web服务。一.Web服务概述: Web服务是一种新的Web应用程序分支,它们是自包含、自描述、模块化的应用,可以发布、定位...
  • 如何从RNN起步,一步一步通俗理解LSTM

    万次阅读 多人点赞 2019-05-06 23:47:54
    如何从RNN起步,一步一步通俗理解LSTM 前言 提到LSTM,之前学过的同学可能最先想到的是ChristopherOlah的博文《理解LSTM网络》,这篇文章确实厉害,网上流传也相当之广,而且当你看过了网上很多关于LSTM的文章...

                                 如何从RNN起步,一步一步通俗理解LSTM

     

     

    前言

    提到LSTM,之前学过的同学可能最先想到的是ChristopherOlah的博文《理解LSTM网络》,这篇文章确实厉害,网上流传也相当之广,而且当你看过了网上很多关于LSTM的文章之后,你会发现这篇文章确实经典。不过呢,如果你是第一次看LSTM,则原文可能会给你带来不少障碍:

    一者,一上来就干LSTM,不少读者可能还没理解好RNN。基于此,我们可以从最简单的单层网络开始学起;
    二者,原文没有对LSTM的三个门解释的足够细致,包括三个不同的sigmoid函数用的同一个符号σ(有错么?没错,看多了就会明白这是习惯用法);
    三者,不同的权值也用的同一个符号w,而当把图、公式、关系一一对应清楚后,初学就不会一脸懵逼了。甚至我们把各个式子的计算过程用有方向的水流表示出来,则会好懂不少,这个时候就可以上动图。

    而我自己就是这么经历过来的,虽然学过了不少模型/算法,但此前对LSTM依然很多不懂,包括一开始反复看ChristopherOlah博文《理解LSTM网络》,好在和我司AI Lab陈博士反复讨论之后,终于通了!

    侧面说明,当一个人冥思苦想想不通时,十之八九是因为看的资料不够通俗,如果还是不行,则问人,结果可能瞬间领悟,这便是教育的意义,也是我们做七月在线的巨大价值。

    众所周知,我们已经把SVM、CNN、xgboost、LSTM等很多技术,写的/讲的国内最通俗易懂了,接下来,我们要把BERT等技术也写的/讲的国内最通俗易懂,成为入门标准,而且不单单是从NNLM \rightarrow Word2Vec \rightarrow Seq2Seq \rightarrow Seq2Seq with Attention \rightarrow Transformer \rightarrow Elmo \rightarrow GPT \rightarrow BERT,我们希望给所有AI初学者铺路:一步一个台阶,而不是出现理解断层。

    本文在ChristopherOlah的博文及@Not_GOD 翻译的译文等文末参考文献的基础上做了大量便于理解的说明/注解(这些说明/注解是在其他文章里不轻易看到的),一切为更好懂。

     

    一、RNN

    1.1 从单层网络到经典的RNN结构
    在学习LSTM之前,得先学习RNN,而在学习RNN之前,首先要了解一下最基本的单层网络,它的结构如下图所示:

    输入是x,经过变换Wx+b和激活函数f,得到输出y。相信大家对这个已经非常熟悉了。

    在实际应用中,我们还会遇到很多序列形的数据:


    如:

    1. 自然语言处理问题。x1可以看做是第一个单词,x2可以看做是第二个单词,依次类推。
    2. 语音处理。此时,x1、x2、x3……是每帧的声音信号。
    3. 时间序列问题。例如每天的股票价格等等。

    而其中,序列形的数据就不太好用原始的神经网络处理了。

    为了建模序列问题,RNN引入了隐状态h(hidden state)的概念,h可以对序列形的数据提取特征,接着再转换为输出。

    先从h_{1}的计算开始看:


    图示中记号的含义是:
    a)圆圈或方块表示的是向量。
    b)一个箭头就表示对该向量做一次变换。如上图中h_{0}x_{1}分别有一个箭头连接,就表示对h_{0}x_{1}各做了一次变换。
    在很多论文中也会出现类似的记号,初学的时候很容易搞乱,但只要把握住以上两点,就可以比较轻松地理解图示背后的含义。

    h_{2}的计算和h_{1}类似。但有两点需要注意下:

    1. 在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点,一定要牢记;
    2. 而下文马上要看到的LSTM中的权值则不共享,因为它是在两个不同的向量中。而RNN的权值为何共享呢?很简单,因为RNN的权值是在同一个向量中,只是不同时刻而已。


    依次计算剩下来的(使用相同的参数U、W、b):


    我们这里为了方便起见,只画出序列长度为4的情况,实际上,这个计算过程可以无限地持续下去。

    我们目前的RNN还没有输出,得到输出值的方法就是直接通过h进行计算:


    正如之前所说,一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h1进行一次变换,得到输出y1。

    剩下的输出类似进行(使用和y1同样的参数V和c):

    OK!大功告成!这就是最经典的RNN结构,是x1, x2, .....xn,输出为y1, y2, ...yn,也就是说,输入和输出序列必须要是等长的。

    1.2 RNN的应用
    人类并不是每时每刻都从一片空白的大脑开始他们的思考。在你阅读这篇文章时候,你都是基于自己已经拥有的对先前所见词的理解来推断当前词的真实含义。我们不会将所有的东西都全部丢弃,然后用空白的大脑进行思考。我们的思想拥有持久性。

    传统的神经网络并不能做到这点,看起来也像是一种巨大的弊端。例如,假设你希望对电影中的每个时间点的时间类型进行分类。传统的神经网络应该很难来处理这个问题:使用电影中先前的事件推断后续的事件。循环神经网络RNN解决了这个问题。

    通过上文第一节我们已经知道,RNN是包含循环的网络,在这个循环的结构中,每个神经网络的模块,读取某个输入,并输出一个值(注:输出之前由y表示,从此处起,改为隐层输出h表示),然后不断循环。循环可以使得信息可以从当前步传递到下一步。

    这些循环使得RNN看起来非常神秘。然而,如果你仔细想想,这样也不比一个正常的神经网络难于理解。RNN可以被看做是同一神经网络的多次复制,每个神经网络模块会把消息传递给下一个。所以,如果我们将这个循环展开:

    链式的特征揭示了RNN本质上是与序列和列表相关的。他们是对于这类数据的最自然的神经网络架构。

    1.3 RNN的局限:长期依赖(Long-TermDependencies)问题

    RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果RNN可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。

    有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测“the clouds are in the sky”最后的词,我们并不再需要其他的信息,因为很显然下一个词应该是sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN可以学会使用先前的信息。


    但是同样会有一些更加复杂的场景。假设我们试着去预测“I grew up in France...I speak fluent French”最后的词。当前的信息建议下一个词可能是一种语言的名字,但是如果我们需要弄清楚是什么语言,我们是需要先前提到的离当前位置很远的France的上下文的。这说明相关信息和当前预测位置之间的间隔就肯定变得相当的大。

    不幸的是,在这个间隔不断增大时,RNN会丧失学习到连接如此远的信息的能力。


    在理论上,RNN绝对可以处理这样的长期依赖问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN肯定不能够成功学习到这些知识。Bengio,etal.(1994)等人对该问题进行了深入的研究,他们发现一些使训练RNN变得非常困难的相当根本的原因。

    换句话说, RNN 会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步。

    因此,如果你正在尝试处理一段文本进行预测,RNN 可能从一开始就会遗漏重要信息。在反向传播期间(反向传播是一个很重要的核心议题,本质是通过不断缩小误差去更新权值,从而不断去修正拟合的函数),RNN 会面临梯度消失的问题。

    因为梯度是用于更新神经网络的权重值(新的权值 = 旧权值 - 学习率*梯度),梯度会随着时间的推移不断下降减少,而当梯度值变得非常小时,就不会继续学习。​

    换言之,在递归神经网络中,获得小梯度更新的层会停止学习—— 那些通常是较早的层。 由于这些层不学习,RNN 可以忘记它在较长序列中看到的内容,因此具有短时记忆。

    而梯度爆炸则是因为计算的难度越来越复杂导致。

    然而,幸运的是,有个RNN的变体——LSTM,可以在一定程度上解决梯度消失和梯度爆炸这两个问题!

     

     

    二、LSTM网络
    Long ShortTerm 网络——一般就叫做LSTM——是一种RNN特殊的类型,可以学习长期依赖信息。当然,LSTM和基线RNN并没有特别大的结构不同,但是它们用了不同的函数来计算隐状态。LSTM的“记忆”我们叫做细胞/cells,你可以直接把它们想做黑盒,这个黑盒的输入为前状态和当前输入。这些“细胞”会决定哪些之前的信息和状态需要保留/记住,而哪些要被抹去。实际的应用中发现,这种方式可以有效地保存很长时间之前的关联信息。

    2.1 什么是LSTM网络

    举个例子,当你想在网上购买生活用品时,一般都会查看一下此前已购买该商品用户的评价。


    当你浏览评论时,你的大脑下意识地只会记住重要的关键词,比如“amazing”和“awsome”这样的词汇,而不太会关心“this”、“give”、“all”、“should”等字样。如果朋友第二天问你用户评价都说了什么,那你可能不会一字不漏地记住它,而是会说出但大脑里记得的主要观点,比如“下次肯定还会来买”,那其他一些无关紧要的内容自然会从记忆中逐渐消失。

    而这基本上就像是 LSTM 或 GRU 所做的那样,它们可以学习只保留相关信息来进行预测,并忘记不相关的数据。简单说,因记忆能力有限,记住重要的,忘记无关紧要的。


    LSTM由Hochreiter&Schmidhuber(1997)提出,并在近期被AlexGraves进行了改良和推广。在很多问题,LSTM都取得相当巨大的成功,并得到了广泛的使用。
    LSTM通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是LSTM的默认行为,而非需要付出很大代价才能获得的能力!

    所有RNN都具有一种重复神经网络模块的链式的形式。在标准的RNN中,这个重复的模块只有一个非常简单的结构,例如一个tanh层。

    激活函数 Tanh 作用在于帮助调节流经网络的值,使得数值始终限制在 -1 和 1 之间。


    LSTM同样是这样的结构,但是重复的模块拥有一个不同的结构。具体来说,RNN是重复单一的神经网络层,LSTM中的重复模块则包含四个交互的层,三个Sigmoid 和一个tanh层,并以一种非常特殊的方式进行交互。

    上图中,σ表示的Sigmoid 激活函数与 tanh 函数类似,不同之处在于 sigmoid 是把值压缩到0~1 之间而不是 -1~1 之间。这样的设置有助于更新或忘记信息:

    • 因为任何数乘以 0 都得 0,这部分信息就会剔除掉;
    • 同样的,任何数乘以 1 都得到它本身,这部分信息就会完美地保存下来。

    相当于要么是1则记住,要么是0则忘掉,所以还是这个原则:因记忆能力有限,记住重要的,忘记无关紧要的

    此外,对于图中使用的各种元素的图标中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。粉色的圈代表pointwise的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。


    2.2 LSTM的核心思想
    LSTM的关键就是细胞状态,水平线在图上方贯穿运行。
    细胞状态类似于传送带。直接在整个链上运行,只有一些少量的线性交互。信息在上面流传保持不变会很容易。

    LSTM有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。门是一种让信息选择式通过的方法。他们包含一个sigmoid神经网络层和一个pointwise乘法的非线性操作。

    如此,0代表“不许任何量通过”,1就指“允许任意量通过”!从而使得网络就能了解哪些数据是需要遗忘,哪些数据是需要保存。

    LSTM拥有三种类型的门结构:遗忘门/忘记门、输入门和输出门,来保护和控制细胞状态。下面,我们来介绍这三个门。

     


    三、逐步理解LSTM
    3.1 忘记门
    在我们LSTM中的第一步是决定我们会从细胞状态中丢弃什么信息。这个决定通过一个称为“忘记门”的结构完成。该忘记门会读取上一个输出和当前输入,做一个Sigmoid 的非线性映射,然后输出一个向量f_{t}(该向量每一个维度的值都在0到1之间,1表示完全保留,0表示完全舍弃,相当于记住了重要的,忘记了无关紧要的),最后与细胞状态相乘。

    类比到语言模型的例子中,则是基于已经看到的预测下一个词。在这个问题中,细胞状态可能包含当前主语的性别,因此正确的代词可以被选择出来。当我们看到新的主语,我们希望忘记旧的主语,进而决定丢弃信息。

    大部分初学的读者看到这,可能会有所懵逼,没关系,我们分以下两个步骤理解:

    1. 对于上图右侧公式中的权值W_{f},准确的说其实是不共享,即是不一样的。有的同学可能第一反应是what?别急,我展开下你可能就瞬间清晰了,即:f_{t} = \sigma (W_{fh}h_{t-1} + W_{fx} x_{t} + b_{f})
    2. 至于右侧公式和左侧的图是怎样的一个一一对应关系呢?如果是用有方向的水流表示计算过程则将一目了然,上动图!红圈表示Sigmoid 激活函数,篮圈表示tanh 函数:


    3.2 输入门
    下一步是确定什么样的新信息被存放在细胞状态中。这里包含两个部分:
    第一,sigmoid层称“输入门层”决定什么值我们将要更新;
    第二,一个tanh层创建一个新的候选值向量,会被加入到状态中。
    下一步,我们会讲这两个信息来产生对状态的更新。

    在我们语言模型的例子中,我们希望增加新的主语的性别到细胞状态中,来替代旧的需要忘记的主语,进而确定更新的信息。

    继续分两个步骤来理解:

    1. 首先,为便于理解图中右侧的两个公式,我们展开下计算过程,即i_{t} = \sigma (W_{ih}h_{t-1} + W_{ix}x_{t} + b_{i})\tilde{C_{t}} = tanh(W_{Ch}h_{t-1} + W_{Cx}x_{t} + b_{C})
    2. 其次,上动图!


    3.3 细胞状态
    现在是更新旧细胞状态的时间了,更新为。前面的步骤已经决定了将会做什么,我们现在就是实际去完成。
    我们把旧状态与相乘,丢弃掉我们确定需要丢弃的信息。接着加上。这就是新的候选值,根据我们决定更新每个状态的程度进行变化。
    在语言模型的例子中,这就是我们实际根据前面确定的目标,丢弃旧代词的性别信息并添加新的信息的地方,类似更新细胞状态。

    再次动图!


    3.4 输出门
    最终,我们需要确定输出什么值。这个输出将会基于我们的细胞状态,但是也是一个过滤后的版本。

    首先,我们运行一个sigmoid层来确定细胞状态的哪个部分将输出出去。
    接着,我们把细胞状态通过tanh进行处理(得到一个在-1到1之间的值)并将它和sigmoid门的输出相乘,最终我们仅仅会输出我们确定输出的那部分。

    在语言模型的例子中,因为他就看到了一个代词,可能需要输出与一个动词相关的信息。例如,可能输出是否代词是单数还是负数,这样如果是动词的话,我们也知道动词需要进行的词形变化,进而输出信息。

    依然分两个步骤来理解:

    1. 展开图中右侧第一个公式,o_{t} = \sigma (W_{oh}h_{t-1} + W_{ox}x_{t} + b_{o})
    2. 最后一个动图:

     


    四、LSTM的变体
    我们到目前为止都还在介绍正常的LSTM。但是不是所有的LSTM都长成一个样子的。实际上,几乎所有包含LSTM的论文都采用了微小的变体。差异非常小,但是也值得拿出来讲一下。
    4.1 peephole连接与coupled
    其中一个流形的LSTM变体,就是由Gers&Schmidhuber(2000)提出的,增加了“peepholeconnection”。是说,我们让门层也会接受细胞状态的输入。

    peephole连接

    上面的图例中,我们增加了peephole到每个门上,但是许多论文会加入部分的peephole而非所有都加。

    另一个变体是通过使用coupled忘记和输入门。不同于之前是分开确定什么忘记和需要添加什么新的信息,这里是一同做出决定。我们仅仅会当我们将要输入在当前位置时忘记。我们仅仅输入新的值到那些我们已经忘记旧的信息的那些状态。

     

    4.2 GRU
    另一个改动较大的变体是GatedRecurrentUnit(GRU),这是由Cho,etal.(2014)提出。它将忘记门和输入门合成了一个单一的更新门。同样还混合了细胞状态和隐藏状态,和其他一些改动。最终的模型比标准的LSTM模型要简单,也是非常流行的变体。

    为了便于理解,我把上图右侧中的前三个公式展开一下

    1. z_{t} = \sigma (W_{zh}h_{t-1} + W_{zx}x_{t})
    2. r_{t} = \sigma (W_{rh}h_{t-1} + W_{rx}x_{t})
    3. \tilde{h} = tanh(W_{rh}(r_{t}h_{t-1}) + W_{x}x_{t})

    这里面有个小问题,眼尖的同学可能看到了,z_{t}r_{t}都是对h_{t-1}x_{t}做的Sigmoid非线性映射,那区别在哪呢?原因在于GRU把忘记门和输入门合二为一了,而z_{t}是属于要记住的,反过来1- z_{t}则是属于忘记的,相当于对输入h_{t-1}x_{t}做了一些更改/变化,而r_{t}则相当于先见之明的把输入h_{t-1}x_{t}z_{t}/1- z_{t}对其做更改/变化之前,先事先存一份原始的,最终在\tilde{h}那做一个tanh变化。

    这里只是部分流行的LSTM变体。当然还有很多其他的,如Yao,etal.(2015)提出的DepthGatedRNN。还有用一些完全不同的观点来解决长期依赖的问题,如Koutnik,etal.(2014)提出的ClockworkRNN。

    要问哪个变体是最好的?其中的差异性真的重要吗?Greff,etal.(2015)给出了流行变体的比较,结论是他们基本上是一样的。Jozefowicz,etal.(2015)则在超过1万种RNN架构上进行了测试,发现一些架构在某些任务上也取得了比LSTM更好的结果。

     

     

    五、LSTM相关面试题

    为帮助大家巩固以上的所学,且助力找AI工作的朋友,特从七月在线AI题库里抽取以下关于LSTM的典型面试题,更具体的答案参见:https://www.julyedu.com/search?words=LSTM(打开链接后,勾选“面试题”的tab)。

    1. LSTM结构推导,为什么比RNN好?
    2. GRU是什么?GRU对LSTM做了哪些改动?
    3. LSTM神经网络输入输出究竟是怎样的?
    4. 为什么LSTM模型中既存在sigmoid又存在tanh两种激活函数,而不是选择统一一种sigmoid或者tanh?这样做的目的是什么?
    5. 如何修复梯度爆炸问题?
    6. 如何解决RNN梯度爆炸和弥散的问题?

     

     

    六、参考文献

    1. ChristopherOlah的博文《理解LSTM网络
    2. @Not_GOD 翻译ChristopherOlah的博文《理解LSTM网络
    3. RNN是怎么从单层网络一步一步构造的?
    4. 通过一张张动图形象的理解LSTM
    5. 如何理解LSTM网络(超经典的Christopher Olah的博文之July注解版)
    6. LSTM相关的典型面试题:https://www.julyedu.com/search?words=LSTM(打开链接后,勾选“面试题”的tab)
    7. 如何理解反向传播算法BackPropagation
    展开全文
  • openfire 聊天记录开发源码 ,包括说明文档,一步一步跟我走绝对能搞定,只为求分
  • 一步一步学Linq to sql

    2012-01-08 22:35:01
    一步一步学Linq to sql. 其中包含了10个文档,教会你一步步学习Linq to sql
  • 一步一步认识用户画像

    万次阅读 热门讨论 2016-11-25 00:22:28
    一步一步认识用户画像标签: 用户画像 GBDT 建模 Python作为一名资深吃瓜群众,身处大数据时代,过去一直不知道用户画像的存在。用户画像到底是何来头,下面会一一告诉你~

    一步一步认识用户画像

    标签: 用户画像 GBDT 建模 Python


    作为一名资深吃瓜群众,身处大数据时代,过去一直不知道用户画像的存在。用户画像到底是何来头,下面会一一告诉你~
    在这里插入图片描述

    用户画像

    1.什么是用户画像?

    交互设计之父AlanCooper最早提出了用户画像(persona)的概念,认为“用户画像是真实用户的虚拟代表,是建立在一系列真实数据之上的目标用户模型”。通过对用户多方面的信息的了解,将多种信息集合在一起并形成在一定类型上的独特的特征与气质,这就形成了用户的独特的“画像”。看下图会更加直接☟☟☟☟
    用户画像简介

    早期的用户画像相对简单,类似于个人档案信息,区分度和可用性都不强。但是随着大数据的发展,数据量的指数型增长和大数据分析技术的逐渐成熟使得用户可捕捉的行为数据越来越多,用户画像才慢慢地发挥其应有的效果。

    2.为什么要用用户画像?

    目前用户画像主要应用于商业领域,其核心工作是为用户打标签,打标签的目的之一是为了让人能够理解并且最重要的是方便计算机处理。标签提供了一种便捷的方式,使得计算机能够程序化处理与人相关的信息,甚至通过算法、模型能够“理解”人。当计算机具备这样的能力后,无论是搜索引擎推荐引擎广告投放等各种应用领域,都将能进一步提升精准度,提高信息获取的效率。通过对人群基本属性行为习惯商业价值等多种维度信息数据综合分析,精准的进行目标受众的画像和定位,能够实现基于大数据的精准营销,这也就是用户画像的用处所在了。

    3.如何实现用户画像?

    一个标签通常是人为规定高度精炼的特征标识,如年龄段标签:25~35岁,地域标签:北京,标签呈现出两个重要特征(看下图):
    在这里插入图片描述
    人为制定标签规则,并能够通过标签快速读出其中的信息,机器方便做标签提取聚合分析。所以,用户画像,也称用户标签,为我们提供了一种简洁的方式去描述用户信息。
    懂得了如何处理用户信息之后,接下来如何实现用户画像呢?首先我们需明确我们的画像方向,例如:影迷偏好、消费者能力等等,人工设计画像的方向,可方便后面的数据采集以及模型构建。详细请看下图:
    在这里插入图片描述
    主要的思路就是:
    用户微观画像的建立→用户画像的标签建模→用户画像的数据架构

    4.标签与用户画像的联系

    提了这么久的标签,那么标签和用户画像到底是什么关系呢?简单来说,它们是整体和局部的关系,用户画像是整体,标签是局部,这种关系通过“标签体系”体现。整体和局部总包含两方面的关系:
    (1)化整为零:整体如何反映在局部;
    (2)化零为整:局部如何组成整体。
    举例来说:“人有一双眼睛一个鼻子”,那么:
    (1)化整为零:对每个人都应该观察到一双眼睛和一个鼻子;
    (2)化零为整:只有位置合适的一双眼睛和一个鼻子我们才认为他是一个人。
    标签建模是用户画像中核心的一块:
    在这里插入图片描述

    5.用户画像如何进行验证?

    用户画像的验证主要是以下两个方面:
    (1)标签打得准不准
    (2)标签打得全不全
    但目前的重点还是放在准确性的验证上~
    在这里插入图片描述

    6.用户画像中对基本属性的预测方式

    懂得了理论之后,当然不仅仅纸上谈兵,用到实处才是硬道理~用户画像目前采用机器学习模型+少量的人工规则来实现的~
    那么接下来我将迈出用户画像建模的一小步,使用GBDT算法(前一篇博客有所提及)来实现对性别这个基本属性的预测。
    主要分为以下几个步骤:
    (1)训练和测试语料准备
    (2)特征选择与特征权重计算
    (3)线下模型训练与测试
    (4)线下效果评估
    (5)线上应用
    (6)线上效果评估
    具体的操作如下:
    安装xgboost-Python(xgboost是GBDT算法的实现,适合用于大量数据处理中,在前一篇博客也有所提及)
    (1)安装基本开发工具
    在终端中输入:

    $ sudo apt install git build-essential python-dev python-setuptools python-pip python-numpy python-scipy
    

    (2)从Github下载最新源代码:

    $ git clone --recursive https://github.com/dmlc/xgboost
    

    (3)编译:

    $ cd xgboost
    $ make -j4
    

    (4)测试:
    此处输入图片的描述
    (5)实现代码(简单的预测):

    import xgboost as xgb
    # read in data
    dtrain = xgb.DMatrix('demo/data/agaricus.txt.train')
    dtest = xgb.DMatrix('demo/data/agaricus.txt.test')
    # specify parameters via map
    param = {'max_depth':2, 'eta':1, 'silent':1, 'objective':'binary:logistic' }
    num_round = 2
    bst = xgb.train(param, dtrain, num_round)
    # make prediction
    preds = bst.predict(dtest)
    

    (6)深层次的预测:包括进行特征提取、添加缺失值

    import datetime
    
    import pandas as pd
    
    import numpy as np
    
    from sklearn.cross_validation import train_test_split
    
    import xgboost as xgb
    
    import random
    
    from operator import itemgetter
    
    import zipfile
    
    from sklearn.metrics import roc_auc_score
    
    import time
    
    random.seed(3)​
    
    training_filename = 'C:\\Users\\ganda\\Desktop\\train.csv'
    
    testing_filename = 'C:\\Users\\ganda\\Desktop\\test.csv'
    
    submission_dir = 'C:\\Users\\ganda\\Desktop\\'
    
    def load_train():
    
        print("Loading training data csv: " + training_filename)
    
        training_data = pd.read_csv(training_filename)
    
        return training_data
    
    def load_test():
    
        #print("Loading training data csv: " + training_filename)
    
        test = pd.read_csv(testing_filename)
    
        # TODO
    
        return test
    
    def create_feature_map(features):
    
        outfile = open('xgb.fmap', 'w')
    
        for i, feat in enumerate(features):
    
            outfile.write('{0}\t{1}\tq\n'.format(i, feat))
    
        outfile.close()
    
    def get_importance(gbm, features):
    
        create_feature_map(features)
    
        importance = gbm.get_fscore(fmap='xgb.fmap')
    
        importance = sorted(importance.items(), key=itemgetter(1), reverse=True)
    
        return importance
    
    def print_features_importance(imp):
    
        for i in range(len(imp)):
    
            print("# " + str(imp[i][1]))
    
            print('output.remove(\'' + imp[i][0] + '\')')
    
    def run_XGBoost(train, test, features,target,random_state=0):
    
        eta = 0.1
    
        max_depth = 5
    
        subsample = 0.8
    
        colsample_bytree = 0.8
    
        start_time = time.time()
    
        print('XGBoost params. ETA: {}, MAX_DEPTH: {}, SUBSAMPLE: {}, COLSAMPLE_BY_TREE: {}'.format(eta, max_depth, subsample, colsample_bytree))
    
        params = {
    
            "objective": "binary:logistic",
    
            "booster" : "gbtree",
    
            "eval_metric": "auc",
    
            "eta": eta,
    
            "max_depth": max_depth,
    
            "subsample": subsample,
    
            "colsample_bytree": colsample_bytree,
    
            "silent": 1,
    
            "seed": random_state
    
        }
    
        num_boost_round = 260
    
        early_stopping_rounds = 20
    
        test_size = 0.1
    
        X_train, X_valid = train_test_split(train, test_size=test_size, random_state=random_state)
    
        y_train = X_train[target]
    
        y_valid = X_valid[target]
    
        #添加缺失数据
    
        dtrain = xgb.DMatrix(X_train[features],y_train,missing = -9999)
    
        dvalid = xgb.DMatrix(X_valid[features],y_valid,missing = -9999)
    
        watchlist = [(dtrain, 'train'), (dvalid, 'eval')]
    
        gbm = xgb.train(params, dtrain, num_boost_round, evals=watchlist, early_stopping_rounds=early_stopping_rounds, verbose_eval=True)
    
        print("Validating...")
    
        check = gbm.predict(xgb.DMatrix(X_valid[features]), ntree_limit=gbm.best_ntree_limit)
    
        score = roc_auc_score(y_valid.values,check)
    
        print('Check error value: {:.6f}'.format(score))
    
        imp = get_importance(gbm, features)
    
        print('Importance array: ', imp)
    
        print("Predict test set...")
    
        test_prediction = gbm.predict(xgb.DMatrix(test[features]), ntree_limit=gbm.best_ntree_limit)
    
        print('Training time: {} minutes'.format(round((time.time() - start_time)/60, 2)))
    
        return test_prediction.tolist(), score
    
    def create_submission(score, test, prediction):
    
        # Make Submission
    
        now = datetime.datetime.now()
    
        sub_file = submission_dir + 'submission_' + str(score) + '_' + str(now.strftime("%Y-%m-%d-%H-%M")) + '.csv'
    
        print('Writing submission: ', sub_file)
    
        f = open(sub_file, 'w')
    
        f.write('id,probability\n')
    
        total = 0
    
        for id in test['id']:
    
            str1 = str(id) + ',' + str(prediction[total])
    
            str1 += '\n'
    
            total += 1
    
            f.write(str1)
    
        f.close()
    
    train = load_train()
    
    test = load_test()
    
    features = [] 
    
    # TODO!
    
    #添加特征​
    
    features.extend([''])
    
    print('Length of train: ', len(train))
    
    print('Length of test: ', len(test))
    
    print('Features [{}]: {}'.format(len(features), sorted(features)))
    
    #label是目标字段​
    
    test_prediction, score = run_XGBoost(train, test, features,'label')
    
    print('Score = {}'.format(score))
    
    create_submission(score, test, test_prediction)
    

    参考资料:
    1.一步步教你看懂大数据时代下的“用户画像”
    2.大数据用户画像方法与实践
    3.Xgboost分类
    4.通过观影兴趣预测用户画像之性别属性

    展开全文
  • 从今天开始,一步一步的搭建一个web网站。计划采用前后端分离开发的方式。包含但不限于以下技术: springboot mybatisPlus redis shiro vue(暂定vue-element-admin) mysql mongodb springcloud(后续多系统分布式...

    从今天开始,一步一步的搭建一个web网站。计划采用前后端分离开发的方式。包含但不限于以下技术:

    1. springboot
    2. mybatisPlus
    3. redis
    4. shiro
    5. vue(暂定vue-element-admin)
    6. mysql mongodb
    7. springcloud(后续多系统分布式时使用)
    8. flowable(流程相关)
    9. swagger
    10. 代码生成工具

    搭建后端服务

    方式一、开发工具是idea

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    然后直接next -》 finish 就可以了
    然后等待maven更新完jar包项目就搭建完成了

    方拾二、直接从官网创建项目

    在这里插入图片描述
    然后直接将生成的项目导入到自己的开发工具中即可。

    测试项目

    写一个测试的controller
    在这里插入图片描述
    启动项目,然后浏览器中输入:http://localhost:8080/helloWorld
    在这里插入图片描述

    大功告成。

    明天再写前端的搭建方式。

    展开全文
  • 包含了2个电子文件:一步一步在Linux上部署11.2.0.3 RAC+Dataguard.pdf一步一步在Linux上部署11.2.0.3 RAC+Dataguard.ppt
  •  最近很长的一段时间,我对下一步的学习方向,或者说java的学习方向,充满了困惑。 困惑1:在工作上遇到的很多技术上的东西,都给我一种感觉----不做不知道,谁做谁都会。要做一个东西,或者遇到一个问题的时候,...
  • 一步一步开发微信小程序

    万次阅读 多人点赞 2018-03-05 12:39:53
    小程序的开发与传统的web前端开发极其相似,想必各位技术宅们...第一步:安装 首先下载微信开发者工具 https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=201716 直接下载安装,点击下一步...
  • 刚刚从网上整理的学习asp.net mvc1.0...一步一步学习ASP.net Mvc1.0.rar包含: 一步一步学习ASP.net Mvc1.0.doc NerdDinner源码.zip 前段时间在CSDN下了比较多的东西,没积分了,所以放了5分资源分,请各位凉解一下
  • 林锋教你一步一步玩机器人(arduino)基础篇pdf,提供“林锋教你一步一步玩机器人(arduino)基础篇”免费资料下载,主要包括Arduino 介绍、Arduino 在国内的发展等内容,可供学习使用。
  • Android教程-从零开始一步一步接入SDK

    万次阅读 多人点赞 2014-08-29 00:28:10
    从零开始一步一步接入SDK
  • 一步一步构建ucore OS(配套代码) "操作系统简单实现与基本原理 — 基于ucore" (持续更新) 介绍 ucore OS是用于清华大学计算机系本科操作系统课程的OS教学试验内容。 ucore OS起源于MIT CSAIL PDOS课题组开发的xv6&...
  • Allegro_PCB_SI 一步一步学会前仿真 这篇文章中需要在JEDEC网站中下载的资源,包括RDIMM的brd文件
  • 包含: Linux网络编程一步一步学.pdf 编辑:刘杰 Linux网络编程基础(一).doc Linux网络编程基础(二).doc Linux网络编程基础(三).doc
  • Java 代码一步一步转为Smali代码

    千次阅读 2018-09-08 16:08:35
    由Java到Smali本来是不需要这么麻烦的,但是为了能对比研究Smali语法,所以,采取Java源代码一步一步编译成Smali代码,对比学习Smali的语法。 一、所需文件 JDK 下载地址: https://www.ora...
  • untangled-devguide, 一步一步交互式Untangled指南 开发人员实时版本 Untangled开发人员指南这是一个交互式开发指南,用于开发应用。 本方法将在本地克隆,因为包含的练习要求你扩展和编辑代码。里面有什么?本指南...
  • Filter Paffis By Nets是进一步筛选要分析的路径范围的设置窗口,用户可以通过选择包括或不包括某条连线的方式进一步筛选要分析路径的范围,如图所示。  图 通过连线逃一步筛选要分析的路径 :
  • 一步一步教你如何在AndroidStudio查看Android源码(AOSP源码)
  • DIY装机训练营,一步一步教你怎么弄。其中包括怎么拆机等等内容,分享的别人的。
  • 一步一步教你VMWare安装苹果Mac OS X

    万次阅读 2018-07-27 22:49:50
    随着iPhone、iPad、Mac等苹果产品越来越火爆,越来越多的初学者想要了解和尝试苹果平台,包括苹果操作系统Mac OS X、苹果演示软件Keynote、苹果开发工具Xcode等。然而,苹果电脑价格昂贵,并不是每个人都可以承受。 ...
  • 一步一步地安装Yeoman脚手架工具

    千次阅读 2015-09-06 17:59:55
    Yeoman包括了三个部分yo(脚手架工具)、grunt(构建工具)、bower(包管理器).前提条件:安装NodeJs、Git Bash工具安装yeoman前先配置package.json文件第一步:安装yeoman命令npm install -g yo报错了,如果不报错...
  • // 不包含主键列 private List<ColumnModel> columns; public TableModel() { } public TableModel(String tableName) { this.tableName = tableName; } public String getTableName() { ...
  • 一步一步搭建客服系统

    千次阅读 2016-05-19 23:27:27
    阅读目录 1 文本视频聊天2 搭建自己的服务器3 发送图片4 客户列表5 ...基于webrtc的客服系统,功能包括:文本聊天,语音视频聊天,发送图片,发送文件,桌面共享,电子白板。     Demo地址: https://www.gw
  • 一步一步写ARM汇编(一)

    万次阅读 多人点赞 2018-06-05 21:18:48
    本博文开始学习一步一步写ARM汇编程序。 一、重要概念理解1. 立即数1)把数据转换成二进制形式,从低到高写成 4位1组的形式,最高位一组不够4位的前面补02)数1的个数,如果大于8个【可能也是立即数,取反】不是...
  • 有时候我们需要执行一系列操作, 每一步需要上一步执行成功后才会执行下一步否则就退出整个脚本, 直接举个栗子: wget https://getcomposer.org/installer || ( echo "failed"; exit ) && ...
  • 包括2部分 需要下载另外一部分
  • 新人学习施耐德plc必备教材,包括HMI,伺服,变频器等通讯例子,学习,助你在PLC的道路上打下坚实的基础。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 936,538
精华内容 374,615
关键字:

一步一步包含什么意思