精华内容
下载资源
问答
  • 触发器 连续相同的输入可以被忽略,只有当输入发生变化时,触发器才会响应输出,例如0变成1,1变成0,触发器将会触发,如果输入连续不变,则触发器不会触发,可以通过触发器实现二进制计数器,译码器等。 时钟 可以...

    IT行业程序开发使用到了很多种高级语言比如Java,C,Python,JavaScript,也有个别领域用到了低级语言即汇编语言,这些都是程序语言,程序语言就是人类能够理解的语言,计算机硬件也有语言即机器指令,这些机器指令只有机器能够理解。

    下面我整理了下程序语言与计算机硬件的层次,梳理一下,就叫做计算机层次结构吧(事先声明:下文介绍的主要PC机器,服务器,手机的计算机层次结构,其他类型的计算机(超级计算机,量子计算机,模拟计算机等)层次结构会有不同,不做介绍)。

    计算机层次结构

    计算机层次结构表示的从应用软件级别到晶体管级别的层次结构,如下图所示,左侧为对应层级的相关从业人员或者使用人员,右侧为层次之间的构成情况,如下图所示

    218799850_1_20210329065003534

    计算机层次结构

    1.1应用

    这个不必细说,目前我们用到的QQ,微信,支付宝,各类企业应用都属于这一层。

    1.2 高级语言

    高级语言不涉及计算机底层硬件,所以你开发好的程序,经过合适的编译器编译后(当然也得有),可以在任何计算机硬件上运行。

    程序语言有C,Java,Phthon等几十种,每一种都有它的应用场景,下面简单介绍C,Java,Phthon的使用场景。

    C语言是高级语言中最底层的,在高级语言中效率也是最高的,主要是用于操作系统,网络协议,驱动程序,监控系统等性能要求比较高的领域,所谓C生万物,几乎所有的其他高级语言的编译器都是基于C实现的,所以C语言可以认为是其他高级语言的祖宗。

    Java语言属于一种解释性和编译性都具备的语言,在一些大型的企业应用,网站用得比较多,通过虚拟机,由虚拟机将Java字节码翻译成机器指令。

    Phthon语言属于一种解释性语言,与Java类似,它也有自己的解释器,将python文件翻译为机器指令,python主要用于一些数据计算,分析,人工智能,运维领域,这门语言简单,好学,不懂计算机知识的人,也可以很快入门。

    1.3 低级语言

    低级语言即汇编语言, 高级语言通过合适的汇编器就可以被翻译成汇编语言,汇编语言是程序语言中最底层了,它与计算机硬件相关,每类计算机硬件(相同的指令集架构的硬件可以归为一类)都有各自的汇编语言,例如基于X86 64位的汇编语言,基于ARM的汇编语言,这里所说的X86,ARM指的是指令集体系架构,下个章节会讲到。

    汇编语言由于跟计算机硬件是一对一的,紧密关联的,因此通常用于一些跟底层硬件紧密相关的开发工作,目的就是充分发挥这些硬件的优势,比如用C语言开发操作系统时,会用到一些汇编指令,这些汇编指令是某类计算机硬件独有的,通过这些汇编指令,能高效地利用这类计算机硬件的优势,因此汇编语言不能跨硬件。

    1.4 机器指令/微体系架构

    机器指令是二进制指令,指令的长度通常跟硬件的位数相同,例如X86 64位就表示机器指令的长度是64位,高级语言可以直接通过编译器翻译成机器指令,也可以先通过汇编器翻译成汇编语言,然后再将汇编语言翻译成机器指令,再就是对于一些解释性语言如Java,可以通过编译器翻译为中间指令(字节码),Java的解释器可以解释这些中间指令为机器指令,如下图为C语言和Java语言的编译和解释过程。

    218799850_2_20210329065003628

    C语言的编译过程

    218799850_3_20210329065003816

    Java语言解释过程

    微体系架构是什么呢,先来说说指令集体系架构(ISA)吧,指令集体系架构就是从汇编程序员的角度看计算机硬件,即计算机硬件提供些什么机器指令,有多少种指令,每种指令的格式,数据和指令的长度(64位还是32位),有哪些寄存器存储临时数据,数据以什么样的方式进行存储等,它是一套标准规范。

    微体系架构就是指令集体系架构的具体实现,汇编程序员只知道采用 ADD P,Q,N的汇编指令可以实现加法运算,不需要考虑它是怎么实现的,具体实现由微体系架构进行处理。

    1.5 晶体管

    晶体管是一种控制电流流动的装置,其实就是一个开关,打开开关,电流通了,关闭开关,电流断了,怎么来打开和关闭开关呢,是通过电子的方式来实现的,主要原理就是通过在晶体硅的基础上,利用P型和N型,氧化硅(一种绝缘玻璃)以及晶体管连接到其他部分的金属导线,形成MOSFET电路,是不是有点抽象,可以忽略这个概念,只要明白晶体管就是一个电子开关就可以了,看下图

    218799850_4_20210329065003925

    正极晶体管

    当栅极加正电压时,源极与漏极之间的电阻减小,电流从源极流向漏极。

    218799850_5_2021032906500481

    负极晶体管

    当栅极加负电压时,源极与漏极之间的电阻减小,电流从源极流向漏极。

    所以可以根据这两类晶体管互相连接,形成各类的逻辑门,以非门为例

    218799850_6_20210329065004175

    非逻辑门

    当输入1电压即正电压时,下方的晶体管电流通过,零电压输出,所以输出0。

    当输入0电压即负电压时,上方的晶体管电流通过,正电压输出,所以输出1。

    其他逻辑门不再阐述,都是通过晶体管连接而成。

    1.6 逻辑门

    正如1.5章节说的,晶体管有两种状态,要么有电流通过,要么没有电流通过,因此可以通过这两种状态进行任意的逻辑组合,可以形成与门,或门,与非门,或非门,非门,异或门等常用的逻辑门。

    218799850_7_20210329065004316

    与门

    A

    0

    0

    1

    1B

    0

    1

    0

    1A and B

    0

    0

    0

    1

    218799850_8_20210329065004363

    或门

    ABA or B

    0

    0

    1

    10

    1

    0

    10

    1

    1

    1

    218799850_9_20210329065004411

    与非门

    A

    0

    0

    1

    1B

    0

    1

    0

    1A nand B

    1

    1

    1

    0

    218799850_10_20210329065004456

    或非门

    A

    0

    0

    1

    1B

    0

    1

    0

    1A nor B

    1

    0

    0

    0

    218799850_11_20210329065004488

    异或门

    A

    0

    0

    1

    1B

    0

    1

    0

    1A xor B

    0

    1

    1

    0

    218799850_12_20210329065004550

    非门

    A

    0

    1not A

    1

    0

    逻辑门可以进行任意连组合,如下图

    218799850_13_20210329065004598

    任意逻辑门组合

    上图有3个输入即X,Y,Z,它的布尔表达式为

    X and (Z nor (not Y))

    将或非逻辑门转化后即 X and not (Z or (not Y)),它的输出真值表为

    X

    0

    0

    0

    0

    1

    1

    1

    1Y

    0

    0

    1

    1

    0

    0

    1

    1Z

    0

    1

    0

    1

    0

    1

    0

    1A

    1

    1

    0

    0

    1

    1

    0

    0B

    0

    0

    1

    0

    0

    0

    1

    0C

    1

    1

    1

    1

    1

    1

    0

    1输出

    0

    0

    0

    0

    0

    0

    1

    0

    1.7 集成电路

    通过逻辑门的任意组合可以形成各类逻辑电路,如锁存器,触发器,时钟等。

    锁存器

    可以通过一个开关,打开开关后,输入的值可以直接连接到输出,关闭开关后,输入的值无效,从而可以保留上一次输出的值,这样的话,多个锁存器组合起来就可以实现寄存器了。

    触发器

    连续相同的输入可以被忽略,只有当输入发生变化时,触发器才会响应输出,例如0变成1,1变成0,触发器将会触发,如果输入连续不变,则触发器不会触发,可以通过触发器实现二进制计数器,译码器等。

    时钟

    可以通过非门实现,非门的输出作为输入,就可以形成连续的0101,持续不断地发送电路信号。

    将任意个逻辑电路进行组合集成就是集成电路,可以形成诸如寄存器,ALU(逻辑算数单元),时钟等,这些设计就比较复杂了,不再详述。

    展开全文
  • 一文懂引用、指针、const、参数传递的关系 前言 在《剑指offer》上看到一个问题:如果写的函数需要传入一个指针,则面试官可能会问是否需要为该指针加上const、把const加在指针不同的位置是否有区别;如果写的...

    一文搞懂引用、指针、const、参数传递的关系


    前言

    在《剑指offer》上看到一个问题:如果写的函数需要传入一个指针,则面试官可能会问是否需要为该指针加上const、把const加在指针不同的位置是否有区别;如果写的函数需要传入的参数是一个复杂类型的实例,则面试官可能会问传入值参数和传入引用参数有什么区别、什么时候需要为传入的引用参数加上const。
    摸摸鼻子,自问自己是一脸懵逼,答不上来的。关于引用、指针、const以及参数传递这方面的知识是混淆不堪的!


    一、const限定符

    1、 const对象一旦创建后其值就不能再改变,const对象必须初始化。

    2、 对象的类型决定了其上的操作,与非const类型所能参与的操作相比,const类型的对象能完成其中大部分,但也不是所有操作都适合。主要限制就是只能在const类型的对象上执行不改变其内容的操作。

    3、 在不改变const对象的操作中还有一种是初始化,利用一个对象去初始化另外一个对象,它们是不是const都无关紧要。


    二、引用是什么?

    1、引用即别名: 引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。

    2、 定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。初始化完成后,引用将和它初始值对象一直绑定在一起。

    3、 引用必须初始化,因为无法令引用重新绑定到另外一个对象。

    4、 引用类型都要和与之绑定的对象严格匹配(有两种例外)。且引用只能绑定在对象上,不能与字面值或者某个表达式计算结果绑定在一起。

    5、const的引用:

    5.1、常量引用: “常量引用” 是 “对const的引用” 的简称。
    5.2、 可以把引用绑定到const对象上,与普通引用不同的是:对常量的引用不能用作修改它所绑定的对象。

        const int ci = 123;
    	const int &r1 = ci;   // 正确:引用及其对应的对象都是常量
    	r1 = 42;              // 错误:r1是对常量的引用
    	int &r2 = ci;         // 错误:试图让一个非常量引用指向一个常量对象
    

    5.3、第一种例外: 初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其允许为一个常量引用绑定非常量的对象、字面值、甚至是一般表达式。

    	int i = 123;
    	const int &r1 = i;       // 允许将const int&绑定到一个普通int对象上
    	const int &r2 = 123;     // 正确:r1是一个常量引用
    	const int &r3 = r1 * 2;  // 正确:r3是一个常量引用
    	int &r4 = r1 * 2;        // 错误:r4是一个普通的非常量引用
    

    原因: 当一个常量引用 ri 被绑定到另外一种类型的对象 ti 上时,编译器会生成一个临时量对象 temp,此时,ri 就被绑定到这个临时量对像 temp 上,这个临时量对象 temp 的类型与 ri 类型是一致的,即 ti 的类型会被转换成引用的类型。当 ri 不是一个常量时,就允许对 ri 赋值,这样就可以改变引用对象的值(本意),但此时 ri 绑定的对象实际上是一个临时量对象 temp,也就与本意相违背。

    5.4、 对const的引用可能引用一个并非const的对象。常量引用仅对引用可参与的操作做出了限定,对引用的对象本身是不是一个常量未作限定。通俗理解就是:对象本身是大名,常量引用是小名,用小名不能修改对象的值,但是大名可以。

    6、参数传递——引用传递:

    6.1、传引用调用:当形参是引用类型时,称它对应的实参被引用传递,引用形参就是它绑定对象(对应实参)的别名。当实参的值被拷贝给形参时,形参实参是两个相互独立的对象,称为传值调用,即值传递。

    6.2、传引用参数:通过使用引用形参,允许函数改变一个或多个实参的值。

    // 该函数接受一个int对象,然后将对象的值设置为0
    void reset(int &i)        
    {
    	i = 0;               // 改变了i所引对象的值
    }
    
    // 直接传入对象,无需传递对象的地址
    int j = 3;
    reset(j);               // j使用传引用,值被改变
    
    

    6.3、使用引用的好处:
    好处一:避免拷贝。 参数传递时,使用值传递,可能会拷贝大的类类型或者容器对象,比较低效,甚至有的类型(IO类型等)不支持拷贝操作。只能通过引用参数的方式访问。

    好处二:返回额外信息。 一个函数只能返回一个值,当需要同时返回多个值,引用形参为我们返回多个结果,提供了有效途径。

    // find_char函数,返回string对象中某个指定字符第一次出现是位置,以及出现次数
    // 返回s中c第一次出现的位置索引
    // 引用形参occurs复杂统计c出现总次数
    string::size_type find_char(const string& s, char c, string::size_type& occurs)
    {
    	auto ret = s.size();       // 第一次出现位置(有的话)
    	occurs = 0;                // 出现次数的形参值
    	for (decltype(ret) i = 0; i != s.size(); ++i)
    	{
    		if (s[i] == c)
    		{
    			if (ret == s.size())
    			{
    				ret = i;       // 记录第一次出现位置
    			}
    			occurs++;          // 出现次数+1
    		}
    	}
    	return ret;                // 隐式返回次数
    }
    

    7、尽量使用常量引用

    7.1、形参的初始化方式和变量的初始化方式是一样的一个普通的引用必须用同类型的对象初始化。如6.2中引用版本的reset,只能使用int类型的对象,不能使用字面值、求值结果为int的表达式、需要转换的对象、const int 类型的对象。使用引用而不是常量引用会极大的限制函数所能接受的实参类型。记住:初始化常量引用时允许用任意表达式作为初始值


    三、指针是什么?

    1、与引用的异同:
    相同点:与引用类似,也实现了对其他对象的间接访问
    不同点:a、指针本身也是一个对象,允许对指针赋值和拷贝,并且可以先后指向不同的对象;b、指针不需要在定义是赋初值。

    2、引用不是对象,不能定义指向引用的指针。

    3、指针的类型要和它指向的对象严格匹配,除了两种例外情况。(和引用类似)

    4、指向指针的引用:指针是对象,存在对指针的引用。

    int i = 22;
    int *p;         // p是一个指针
    int *&r = p;    // r是对指针p的引用
    
    r = &i;         // r引用了一个指针,给r赋值&i就是令p指向i
    *r = 0;         // 解引用r得到i,也就是p指向的对象,将i的值改为0
    

    判断 r 类型到底是什么,从右向左阅读 r 的定义。离变量名最近的符号对变量的类型有直接的影响。如上, r 是一个引用,声明符的其余部分用以确定 r 引用的类型是什么,此例中 * 说明 r 引用的是一个指针。最后,声明的基本数据类型部分指出 r 引用的是一个 int 型指针。

    5、const 和指针

    5.1、指向常量的指针(例外一),与常量引用类似,不能用于改变其对象的值。想要存放常量对象的地址,只能使用指向常量的指针。指向常量的指针可以指向一个非常量对象。(指向常量的指针可以记为:const在左,* 在右,简称const*)

    5.2、const指针: 指针是对象而引用不是,与其他对象类型一样,指针可以把自身定为常量。常量指针(C++Primer这么定义常量指针,与我们常用定义相反)必须初始化 ,初始化完成后,它的值(存放的指向对象的地址)就不可以改变了。(常量指针可以记为:* 在左,const在右,简称*const)

    5.3、指针常量还是常量指针,相信每个人被它俩所迷惑。中文是它俩具有迷惑性的主要原因!!!建议直接记const和 * 两者谁在左谁在右比较好。它俩的作用最好的办法是从右往左阅读去判定。

    int num = 12;
    int *const p = #   // *const,距离变量p最近的是const,即p是一个常量,p保存的地址不可更改,
                           // 再往左就是 *,即const p是一个指针。因此p的值(指向对象的地址)不可以更改,p指向对象的值可以更改。
    
    const int *p = #   // const*,距离变量p最近的是 * ,即p是一个指针,可以更改p的值,
                           // 再往左就是const,即 *p 是一个常量。因此,p的值(指向对象的地址)可以更改,p指向对象的值不可以更改。
    

    6、传递参数——值传递

    6.1、在引用传递部分,已经介绍了引用传递和值传递的区别,指针传递本质就是值传递:它传递的是一个地址,将实参的地址拷贝给形参,对形参(形参存的是地址)的更改不会影响实参。(指针的行为和其他非引用类型一样,当执行指针拷贝操作时,拷贝的是指针的值,拷贝之后,两个指针是不同的指针。)

    6.2、因为指针是我们可以间接的访问它所指的对象,所以通过指针可以修改它所指对象的值。

    int i = 42;
    reset(&i);
    
    void reset(int* ip)
    {
    	*ip = 0;            // 改变指针ip所指对象的值
    	ip = 0;            // 只改变了ip的局部拷贝,实参未改变
    }
    

    7、例外二(引用&指针)

    7.1、存在继承关系的类是一个重要的例外:可以将基类的指针或引用绑定到派生类对象。
    当使用基类的引用(指针)时,实际上我们并不清楚该引用(或指针)所绑定对象的真实类型。该对象可能是基类的对象,也可能是派生类的对象。


    总结

    记录的过程也是学习的过程。还有很多遗漏之处,暂时的就记录到这里。如有错误还请包涵和指正!感谢!!!

    展开全文
  • 2016年,从先后关系上讲,CNTK(Cognitive Toolkit)伸手接过女神的接力棒,可惜魔障难消,用的人少,没有推广开,于2019年停止维护。 GitHub上的悼词是:“在这个版本之后,没有新功能开发的计划。” 这意味着,...

    2021-03-01 14:21:19

    搞深度学习框架的那帮人,不是疯子,就是骗子

    世间有一种软件,名叫“深度学习框架”。

    在人工智能的江湖,常听人言:得框架者,得天下。

    多年以前,一面画着大G的大旗在高处飘扬,美国谷歌公司的深度学习框架占据大半江山。万万没有想到,一场大风暴来了。

    2018 年,脸书公司“同款”对标产品把一款前辈产品吸纳进来,联剑并肩,威力大增。一年后,火力全开,专拣敌人的罅隙进攻。连冲数剑,杀开一个缺口,有守有攻,看看就可闯出。放眼学术圈,更是独领风骚,顶级学术会议的胜利快报像雪片一样飞来。

    小心低头,王冠易掉,谷歌框架的王者时代,结束了。

    历史总是吊诡,一些无名之处会发生极为有名的战役。战事残酷而隐秘,高深晦涩的技术仿佛咒语,牢牢挡住人们的视线。

    美国白宫《2019年国家人工智能研发战略规划》报告中,美国将中国视为人工智能主要对手,进行了深刻观察。

    “中国人工智能发展势头很猛。” 这话猛一听,真让人高兴。

    后半句是个打击:“中国人工智能缺点亦十分明显,硬件、算法、人才……人工智能框架创新能力薄弱。”

    “硬件” “人才”……这题我会,这题我会,“框架”是个啥?

    假如人工智能深度学习是太平洋上的一个岛屿,算法是岛上茂盛的植被,框架和芯片则是地质结构,算法建在框架和芯片之上。

    深度学习框架,头顶两个光环亮闪闪,第一个,基础软件。

    几乎所有的深度学习开发者,都要用深度学习框架。

    几乎所有的深度学习算法和应用,都是用其实现的。

    作为一种复杂基础软件,有这样一条原则:极少数人“造”轮子,大部分人“用”轮子。

    框架研发门槛高不可攀,本质上,这类产品是大型科技企业才“配”拥有的基础设施,小门小户造不起。

    多说一句,打败围棋大师李世石的人工智能阿法狗(AlphaGo)听过吧,框架也是其背后的底层技术。

    谷歌科学家的凡尔赛是:“我们让阿法狗更顺畅”。

     

    1上半场:美国科技大厂的豪门恩怨

    简单地说,深度学习框架=深度学习操作系统。

    世间最流行的两个深度学习框架,谷歌公司的TensorFlow和脸书公司的PyTorch。

    开发者压力山大,

    需要“精通”这两个,

    或至少“熟练”其中一个,

    甚至,“辅修”第三个框架,“选修”第四个。

    谷歌与脸书,作为美国科技企业,其框架产品的流行度,像极了可口可乐和百事可乐。

    快乐肥宅水统治世界,兵家必争之地必属枭雄。大型科技企业想尽一切办法取得技术上的领先优势,深度学习框架不会错过,也无法绕过。

    算法突破、数据爆发、算力增长的“铁人三项”支撑了AI的浪潮,唯一能将“铁人”整合的系统软件,是深度学习框架。

    它好比底座,对下,完成对底层硬件的调度;对上,支持模型搭建。

    人工智能的一堆新玩(算)意(法):人脸识别、图像分类、图像检测与分割、语音识别、广告推荐、GAN、强化学习等等,被封装在软件框架里。

    封装,不是封印。

    孙悟空冲着框架大喊:“人工智能,叫你一声,敢答应吗?”

    Siri(用机械女声回答):穿豹纹超短裙的那位,你有事找我?

    一般来说,只有超大型科技企业才能支撑“操作系统”的开发。

    深度学习的“操作系统”萌芽于高等学府,但早期工业雏形出现在美国科技豪门,是大公司竞争的舞台,也是全球计算机技术精英群体,最精锐部队的角逐。

    使用全国通用感叹词:“卧槽,深度学习框架是硬科技。”

    把全球AI顶级精英俱乐部的会员分成两类:一类是原创AI算法的发明者, 一类是AI框架的发明者。

    前一类是加钱阅读的部分,后一类是本文的重点。

    请大家记住这些名字,因为这些“精神小伙”,对深度学习框架的发展至关重要。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    现任阿里巴巴技术副总裁贾扬清,浙江绍兴人,从初中三年级开始接触电脑,他一直觉得自己学编程挺晚的。

    2002年是他高考那一年,浙江省是高考界的领跑者,清华大学计算机系的分数线很高,他去了清华自动化系。

    在科学界,瑞士是物理和数学领域的领跑者。也在2002年,瑞士戴尔莫尔感知人工智能(Idiap)研究所诞生了第一个机器学习库Torch。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    欧洲最高山脉阿尔卑斯山的雪顶千年不化,山脚下的瑞士名城马蒂尼(Martigny),既是登山爱好者的天堂,又是葡萄酒产区。

    这是个做学术的好地方,自1991年以来,这里的研究所就是全球人工智能和认知智能领域的领导者之一。

    机器学习库Torch,出自“葡萄酒产区”研究所的一份研究报告(三位作者分别是:Ronan Collobert、Samy Bengio、Johnny Mariéthoz)。

    其中一位作者姓本吉奥(Bengio),没错,这位眉毛粗粗的科学家,就是深度学习三巨头之一,约舒亚·本吉奥(Yoshua Bengio)的兄弟。

    2007年他跳槽去了谷歌。

    Torch意为火把,成为框架旷野的第一颗火种。

    “库”(Library)是一系列事先编写好的代码集合,在编程中调用,可以减少重复工作。

    加拿大蒙特利尔大学的深度学习框架的开发,始于2007年,Theano是行业祖师爷。

    框架和图灵奖获得者颇有渊源,约舒亚·本吉奥(Yoshua Bengio)和伊恩·古德费洛(Ian Goodfellow)都有参与Theano。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    库和框架的不同之处,在于境界。

    库是兵器库,框架则是一套武林绝学的世界观,程序员在这个世界观的约束下去练(编)拳(程)法(序),结果被框架所调用。框架接管了程序的主控制流。

    反正,框架比库厉害多了。

    有了框架,才能做到只关注算法的原理和逻辑,不用去费事搞定底层系统、工程的事。生命短暂,都想走捷径。话不能这么说,都996了,生产队里的驴也得歇歇。

    转眼间,贾扬清已经在美国加州大学伯克利分校攻读博士学位。也是在此期间,他开启了计算机视觉的相关研究。

    那时候,他常被一个问题困扰:怎样训练和设计深度学习的网络?为此,贾扬清想造一个通用工具。

    著名的Caffe框架的发音和“咖啡”相似,是“快速特征提取的卷积框架”论文的英文简称。巧合的是,这个框架像咖啡一样流行。

    这是贾扬清第一个C++项目,多年以后,他在阿里巴巴回忆:“最开始的时候没有测试,代码纠错(Debug)成了最痛苦的事。”

    2013年的Caffe框架是他的成名之作。在工业场景的计算机视觉系统上,Caffe 稳健快速,是无可争议的王者。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    这一年,Parameter Server(参数服务器)的两位著名教授走向台前,邢波(Eric Xing)教授和Alex Smola教授,现在两位均在美国卡内基梅隆大学(CMU)任教。

    参数服务器是个编程框架,也支持其他AI算法,对深度学习框架有重要影响。

    高校实验室善于技术创新,深度学习框架的很多精髓创意源于此地。但是,深度学习框架复杂性高、工程量极大,长期负责复杂产品,高校并不擅长。

    事实也证明,多年后,高校出生的深度学习框架,都以某种方式“进入”企业,或者被企业赶超了。

    嗅觉敏锐者,业已出发。

    2015年11月,TensorFlow开源,由谷歌大脑团队开发。谷歌的搜索、油管、广告、地图、街景和翻译的背后,都有其身影。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    谷歌开源AI产品备受瞩目。若论起名的原因,TensorFlow直译,张量(tensor)在图中流动(flow)。由此也可获知,数据流图是框架的重要技术。

    再往细说,数据流图由算子组成,算子又分为大算子和小算子。Caffe是大算子抽象,TensorFlow是小算子抽象。小算子好处是灵活,坏处是性能优化难。

    TensorFlow原创者之一是谷歌天才科学家,杰夫·迪恩(Jeff Dean)。

    为什么说他是天才?

    赞美之词就免了。在2000年下半年的时候,Jeff Dean的代码速度突然激增了40倍,原因是他把自己的键盘升级到了USB 2.0。编译器从来不会给Jeff Dean警告,但Jeff Dean会警告编译器。

    笔者承认,这确实是两个段子,出处无考。

    2015 年是一个重要的年份,何恺明等人的研究成果,突破了边界,在准确率上再创新高,风头一时无二。

    谷歌AI研究员弗朗索瓦·乔莱特(Francois Chollet)几乎是独自完成了著名的Keras 框架的开发,为谷歌再添一条护城河,大有“千秋万代,一统江湖”的势头。

    这时候,喊一嗓子“深度学习是下一个重大技术趋势”,已经没有压倒性的反对意见了。

    美国西雅图素有“阿拉斯加门户”之称,微软公司总部位于西雅图卫星城,从那里开车13个小时就能到达谷歌公司总部所在地山景城。在AI的跑道上,很多人在追赶谷歌,但是,微软既没有好车,也没有弯道,压力大了,方向盘也能捏碎。

    按理说,背靠微软的产品本应有个好前途,框架却都没有流行起来。

    英文单词Minerva的意思是“智慧女神”,这是微软亚研院一个孵化项目的名字,由当时的副院长张峥发起,项目组成员有纽约大学王敏捷和北京大学肖天骏。

    现在张峥在亚马逊上海AI研究院做院长。两名大将也随之前往,现在均是张院长麾下主力。

    后来,就没有后来了。开源(Github)给女神画上了句号。

    2016年,从先后关系上讲,CNTK(Cognitive Toolkit)伸手接过女神的接力棒,可惜魔障难消,用的人少,没有推广开,于2019年停止维护。

    GitHub上的悼词是:“在这个版本之后,没有新功能开发的计划。”

    这意味着,微软已经放弃了CNTK。

    两次前车之鉴,微软仍没有认输的打算。

    因为深知框架的重要性,也因为微软的电脑里,绝不会长期使用贴着别人家logo的AI工具。

    2016年,贾扬清从谷歌TensorFlow团队离职,跳槽到了Facebook公司。与谷歌挥手道别,四载光阴(实习两年,工作两年),往事依稀,他的内心充满感怀。

    西雅图作为美国的超一线城市,华盛顿大学是城市招牌之一,华人武术宗师李小龙就毕业于此。“天才少年”陈天奇也在这里取得了计算机博士学位。

    陈天奇在AI圈的名气,不比李小龙在武术界低,且都是少年成名。

    陈天奇读博士的第二年,一个叫做MXNet的项目开始了,这是一个名牌大学联合学术项目。

    仅仅一年时间里,就做出了完整的架构。团队中还有一位闻名遐迩的大神,李沐(现任亚马逊公司资深主任科学家,principal scientist)。

    2016年5月,MXNet开源,浓缩了当时的精华,合并了几个原来有的项目,陈天奇cxxnet、参数服务器、智慧女神、颜水成学生林敏的purine2。

    所以,MXNet,读作“mixnet”,mix是中文“混合”之意。

    可巧了,从华盛顿大学到亚马逊公司全球总部不到6公里,开车只消10分钟。总部大楼抱着两个“温室大球”坐落于市中心。可能是近水楼台先得月,这次亚马逊公司火眼金睛,行动迅速。2017年9月,MXNe被亚马逊选为官方开源平台。

    江山代有才人出,该退休时就退休。同一年,祖师爷Theano官宣退休。

    这时候,贾扬清借鉴谷歌TensorFlow框架里面的一些新思想,实现了一个全新的开源Caffe2。三十而立的他,成长为遍历世界级产品的第一高手。

    谷歌TensorFlow在人间潇洒走一回。未曾想一场大风暴正在酝酿。

    2018 年,PyTorch接纳Caffe2后,意外崛起,上演令谷歌框架王冠落地的戏剧性一幕。

    易用性确实可以抢客户,但谷歌没有想到脸书抢了这么多。

    后来者确实可以居上,但谷歌没有想到脸书仅用如此短的时间。

    改旗易帜,有人哗然,有人唏嘘。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    谷歌出发最早,为何没有独坐钓鱼台?为什么是脸书抢了市场?

    谷歌野心非常大,初期想做很大很全的工具。虽然完备性很强,但是,系统过度复杂。虽然以底层操作为主,有很多基础的功能,但是这些功能没能封装得很好,需要开发者自己解决(定义),手动工作过多。

    三个AI开发者凑在一起,花生配酒,吐槽谷歌TensorFlow,十有八九。

    甲有点激动,说:“实在太难用了,想骂脏话。”

    乙表示赞同,说:“简直就是一个缝合怪。”

    “一座屎山,还要往屎上堆屎。”丙说完,深埋头,叹口气。

    虽然TensorFlow可直接使用天下排名第一又易上手的Python语言来编写程序,算子库丰富,TPU加速,但是,一些个性化规定琐碎,新概念层出不穷,开发者要视其为一种新的编程语言来学习。

    再者,系统非常复杂,代码又长又臭,难以维护。更糟的是,API很不稳定,易变脸。API好比电脑键盘,键盘上的字母位置天天变,谁受得了?你想要一个活着的祖宗吗?

    仅仅是丢市场还不够惨,PyTorch框架带火了背后的技术(动态执行等),脸书开始左右技术趋势。

    谷歌仰天长啸,潸然泪下,口中默念:“万万没有想到。”

    命运像水车的轮子一样旋转,有时高,有时低,而亚马逊公司的MXNet从来没高过。

    知乎上有两篇非常火的高赞帖,可一窥其端倪。

    李沐:《为什么强大的MXNet一直火不起来?》。

    贾扬清:《如何看待亚马逊 AI 李沐团队大批人员离职?》。

    谈起亚马逊和MXNet框架的缘分,就不得不提起一位美国卡内基梅隆大学的高人,Alex Smola教授,他也是李沐在CMU的博士导师。

    2016年7月,Alex Smola教授从CMU重返工业界,加入亚马逊AWS担任副总裁级别的科学家(职级为Distinguished Scientist)。大半年后,2017年3月,李沐加入AWS,直接向老师Alex Smola汇报。

    师徒同框,双手比V。

    此时,巨头已整装列位,兵马齐发。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    微软岂能袖手旁观,微软在智慧女神和CNTK两次滑铁卢之后,依然斗志昂扬准备第三次入局。

    这次,微软思路清奇地设计了ONNX(全称Open Neural Network Exchange),一种开放式深度学习神经网络模型的格式,用于统一模型格式标准。

    ONNX是脸书和微软合作力推的,贾扬清也是发起者之一,目标剑指“标准和生态”。

    说白了,一个PyTorch模型可以被导出ONNX文件格式的模型。

    不止于此,随后,微软基于ONNX这个桥梁研发了一个推理用的ONNX Runtime框架,低调地在2018年最后一个月开源。

    想做“标准”,得大家伙都同意。

    ONNX没成为标准,若论原因,可能是ONNX还做得不够好吧。

    ONNX Runtime框架的“新功能”暴露了巨头之间的动态竞争关系。

    这一次,微软站队脸书,给Pytorch机器学习库做了几个 “好用的部件”。

    若论其中一个原因,可能是微软和脸书没有云上的竞争关系,这几年脸书公司的定位依然还是互联网公司,没有发展云计算。

    亚马逊云(AWS)、谷歌云、微软云则斗红了眼。

    第三次进军框架,微软的策略是,强攻不下,组队打怪。若有一日,Onnx Runtime 框架有希望挑战Pytorch框架,肯定调转火力,支持自家。

    真正的竞争激烈,不是玩家多,而是高手多。短短几年之内,几座技术巅峰,拔地而起,各有各的精绝。

    其一,谷歌和亚马逊是计算图的拥趸。两者都以更高的、令人赞叹的工业级工程质量把计算图抽象推向新高度,把表达能力推向新的里程碑。

    其二,脸书公司在计算过程中没有计算图的概念。但在解决易用性上,超常发挥。

    谷歌皇冠跌落,给后来者“跌出”希望,留给中国队的时间不多了。

     

    2下半场:中国队的出征

    2014年的某一天,北京海淀区丹棱街5号接待了一位特殊的客人。

    这位来自美国CMU的教授,名叫邢波,此时任微软亚研院顾问一职,他擅长的领域包括大规模计算系统。他也是AI科学家俱乐部的白(ding)金(ji)会(da)员(lao)。

    恰在此时,微软亚研院副院长马维英(现任清华大学智能产业研究院讲席教授、首席科学家)找到一位研究员,名叫袁进辉,他是清华大学计算机专业的博士,师从张钹院士。

    知识使人年轻,很多科学家,年逾不惑,双肩包+步行,背影仍像学生。而袁进辉却头发花白,笑容谦和,像是在校园里散步的退休教授,其实他是1981年的。

    马维英副院长和袁进辉谈起,谷歌较先起步,已将大规模主题模型的训练系统技术,应用到谷歌广告系统和推荐系统的关键组件中。邢波教授近期既然到访北京,那不妨合作。

    于是,邢波教授团队和袁进辉团队双剑合璧。这场合作的成果,被表扬了。主管全球研究院的微软副总裁周以真女士评之为该年度看到的最令人激动的成果,不过这是后话。

    那时候的动力,一方面来源于超过谷歌,直道超,没有弯道。另一方面,业界有多位知名科学家和资深工程师,已经在同一问题上酝酿已久。难度可想而知,条件却捉襟见肘。没有可供使用的集群,没有工程师团队的支持。

    按打游戏的说法,微软想上分,那就要看一下配置。推算一下可知,即使是当时最先进的算法,在当时的硬件环境中训练目标规模的模型,至少要半年时间。

    再看一下,双方阵容。

    提起邢波教授的团队,恐怕AI学术圈无人不知,其本人位列论文发表贡献第一(2018),其学生很多已是名校教授,每年发表的论文数量,源源不断地为CMU名列全球大学计算机科学与人工智能的排名第一“贡献力量”。

    “微软代表队”是袁进辉研究员,还有一个实习生高飞。

    这个条件,这个目标,看了只想眯眼说“呵呵”。

    美国宾州匹茨堡和中国北京,时差十几个小时。袁进辉后来回忆:一年多的时间里,每天邮件不断,每周好几次电话会议,技术难题不讨论透彻不罢休。只要足够幸运,就会在错误的道路上迅速挨揍,只要高手够多,不足之处就不会被放过……马维英和刘铁岩两位大佬,羽扇纶巾,幕后帷幄。

    项目结束的时候,2014年已近尾声。大家伙的心声是:“缺少任何一个人,结果都不是大家看到的样子。”

    那一次,袁进辉为破坏式创新的威力,窒息。

    这次合作,成果是LightLDA。它的算法结果是一流的,系统实现是一流的,仅用数十台服务器,完成之前成千上万台服务器才能做的事,所以得到周以真女士的高度评价。

    民(zhi)间(hu)评价:“要我说,LightLDA那是真的正经贡献,又smart,又是解决关键问题,又真work,正经把Topic Modeling(主题模型)在大数据时代的潜力大大地提高了。”

    当时,北京大学计算机科学技术系网络与信息系统研究所,研究分布式系统的肖臻教授也给与LightLDA相当的肯定。这事,被肖臻的学生以敬仰袁进辉大神事迹的口吻在知乎讲过。

    而今复盘,大势的端倪早已显露,大数据、大模型、大型计算架构设计呼之欲出。而这个领域的学者,普遍在2018年才意识到这个问题的重要性。

    微软亚研院不愧为AI黄埔军校,技术前瞻性极强,但是,复杂基础软件的成功,不是仅靠“单刀赴会”。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    大公司必胜,那是夸海口。

    大公司必争,才是真灵验。

    坐标北京西二旗,百度大厦和百度科技园。

    技术大牛背景的李彦宏,牵着搜索入口的现金牛,依着“牛脾气”治理百度,他看不上云计算,这倒让阿里巴巴笑了。

    其实,看不上云计算的技术大佬不止一位,自由开源软件GNU/Linux的鼻祖理查德·斯托曼(Richard Stallman)也多次在公开场合“怼”云计算。

    巧合的是,他俩观点出奇地一致:云计算不是技术创新,而是一种商业模式创新。

    李彦宏睥睨云计算,却对人工智能,满眼小星星。

    百度深度学习研究院(IDL)在人工智能的江湖里,是桃源仙境般的存在,处处大神,遍地高手。高水平科学家、研究人员、工程师密度之大,令人惊叹,感觉连保安都要会编程才配在门口刷工作证。

    昔日盛景,已成绝响。

    时间拉回到2013年,百度第一位T11徐伟,同时也是百度深度学习框架PaddlePaddle的原创者和奠基人。

    每一家科技巨头的深度学习框架的首位指挥官,均非等闲之辈。徐伟也是Facebook早期研究员,Facebook产品矩阵丰富,他负责大规模推荐平台,在多个产品背后显神功。

    可能是有法律文件约束,百度大神科学家的离职,大多不公开原因。徐伟离职加盟地平线,他将手中的接力棒交给了另一位神级技术大牛,撸码一绝的王益。

    见过王益的人会说一个词,“聪明绝顶”,重音在后面两个字上。

    王益在知乎谦虚地自称“四十岁老程序员”,言谈之间一副老技术专家的低调本色。他在加入百度之前曾任谷歌研究员,是少见的“APAC创新奖”获得者(参与开发一个分布式机器学习的工具)。王益是清华大学机器学习和人工智能博士,师从清华大学周立柱教授。

    有一次在知乎分享程序员成长经验,他轻描淡写地说了一句:“我有一位恩师,徐伟。”

    细节总是让人容易忽略,早年,王益曾向徐伟抱怨:“某某团队好像就是想用他们自己研发的工具,不用PaddlePaddle?”

    后来,王益在回复一位网友跟帖时解释当时这一问题存在的合理性:“设计PaddlePaddle是技术换代的时候,步子大,当时来不及优化用户体验,不愿意用确实有道理。离开后,后来人持续优化了体验。内部组织结构调整也促进了新技术的接纳。”

    这也印证了一位百度匿名AIG离职科学家高管对笔者的独家透露:“百度内部曾经有两个类似的产品,最后敲定PaddlePaddle的人,是陆奇。”

    了解此事的人不多,也正因此,采访前夕,这位科学家高管仍在反复向笔者强调——“请务给我匿名”。

    百度最早出发,生态建设也最早起步。

    2017年年末,百度市场部的朋友找笔者交(chi)流(fan),给PaddlePaddle出谋划策。那时候,开源框架的运营和推广已经全面拉开:北航软件学院的教材出版、顶级学术会议模型复现、高校宣讲……

    据说,陆奇离职前,仍然紧盯PaddlePaddle的进展。

    一山行尽,一山青。框架的玩家,不止科技大厂。

    人工智能独角兽旷视科技是从2014年起内部开始研发框架。在2021年的采访中,旷视天元的负责人田忠博告诉笔者:“原因很简单,仅以当时的开源框架,没有办法真正做好科研,才会有自己做深度学习框架的想法。”

    举一例,就能说明问题。

    旷视科技有一篇ShuffleNet的学术论文,仅用Caffe提供的“工具”,永远也探索不到ShuffleNet这件事情的可能性。由此看来,旷视科技早已参悟,研究和工程的共振,离不开强大框架的支持。

    百度PaddlePaddle开源时间点是在2016 年8月。现在看来,这是历史性的一刻,尤其在中美摩擦的历史背景下回看,更不敢皱眉设想,一旦美国忌惮中国的人工智能发展势头,把深度学习框架彻底掐死。

    百度的出征,代表着中国队上场了,标志着中国科技企业参与到人工智能最残酷的战役之中。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    2017年,AI盛极一时,独角兽频现,融资快讯爆炸。而PaddlePaddle作为国内唯一的开源深度学习框架,此后两年多,都是孤家寡人。

    2018年7月,百度成立深度学习技术平台部,由2011年就入职百度的马艳军总负责。

    毕竟是国产框架,2019年,百度PaddlePaddle有了中文名,名叫“飞桨”。国外产品连个中文名都懒得起。

    零的突破之后,新问题是,“用工业级的质量,把创新在框架上实现出来”。

    2019年2月,一流科技获得千万级Pre-A轮投资,袁进辉是创始人兼CEO。此事之后,才有些小道消息传出,早在2017年初,快手创始人宿华就投了一流科技,天使轮。

    “小伙子睡凉炕,全凭火气壮。”一家只有几十人团队的初创公司也来做复杂基础软件。投资人一脸懵逼地进来,一脸懵逼地离开。

    谁都会挑用起来顺手的锤子。框架在一家公司内部很难统一。

    百度内部“军令如山”,必须统一使用飞桨。

    旷视科技内部可以用任何开源框架,员工中自发使用天元框架者居多。

    微软亚研院的情况是:很多工程实现是实习生完成,干活时会让同学们继续用熟悉的框架干活,很难强行统一用CNTK。

    互联网科技公司大多是软件起家,华为则被戏称为“泥腿子们”终于“洗干净脚进城”的硬件厂商。是啥不重要,能打就行。所以,华为要拿出来单聊。

    华为在开源软件世界里,风评不高,前脚还有:“鸿蒙失火,殃及池鱼” (禁止谐音梗,扣钱)。

    华为MindSpore的行动颇为迅速,可惜,在群众情绪上,被鸿蒙拖了后腿。

    2018年10月10日,上海。华为全联接大会上,肯德基外卖全家桶套餐,不对,讲错了,是AI战略与全栈全场景AI解决方案。这是华为高层首次提起MindSpore这个事儿。

    2019年,10月15日,14点02分,王益在网上突然发帖问了一句,这“开源框架”什么时候开源啊?有匿名采访者告诉笔者:“贾扬清回了一句,‘Show me the code’。”笔者没有找到原文或者截图。

    按工作流程,华为MindSpore官方进驻知乎,先发了一个“Read me文档”(翻译为“阅读指南文件”)。结果,人在家中坐,祸从天上来,很多人误以为“开源”只有“Read me”而已,热度直接飞起。

    最息事宁人的评论:“沸腾就完事了,想那么多干嘛。”

    最佳画面感评论:“站在马里亚纳海沟里挥舞道德的内裤。”

    神评论:“按揭开源。”

    网友的才华,从手机屏幕里喷出来。

    哪怕华为员工看到这些评论,也笑出了猪叫,细一想,要克制,便在暗地里捂嘴笑。

    一位老牌厂商高管在采访时,告诉笔者:“华为不了解生态系统对软件的影响。这就是为什么他们在发布手机操作系统时,没有考虑如何构建生态系统。”受访人要求笔者匿名。

    这一评价,一针扎在要害上。

    外国框架并不成熟,也不完美,这也是国产框架参战的部分原因。

    有人发问:“为什么要再做一个框架?”

    华为内部也有人扪心自问:“MindSpore解决的特色问题到底是什么?”

    可能是2020年正式开源前夕,

    可能是华为中央软件院总架构师金雪锋博士、算法科学家于璠博士、开源社区运营团队负责人黄之鹏等人第一次“齐聚”会议室,

    可能是一场“元老会”。

    笔者了解到,在华为内部组织结构中,MindSpore属于昇腾产品团队,也归属于计算产品线。这是一个和华为“小云”同级别的BU。

    匿名采访者透露,MindSpore在内部也是要承接业务部门需求的。

    MindSpore再早之前的研发时间线不得而知,因为“事关”华为最敏感的“部(xin)位(pian)”。

    细细翻阅三位科学家的公开观点,

    第一位,华为MindSpore首席架构师金雪锋博士。

    第二位,一流科技创始人袁进辉博士。

    第三位,谷歌公司Waymo自动驾驶汽车感知和规划任务机器学习平台资深研发工程师、阿帕奇基金会MXNet项目委员会委员、Horovod(是Uber开源的一个深度学习工具)技术委员会委员袁林博士。

    他们共同认为:“市场需求没有很好地满足,技术没有收敛,创新还有空间。”

    国外框架出发时,广阔天地,大有可为,国产框架正好相反。好摘的果实都已被摘走,只剩高高树顶上的,还有那零散摔落在地的。

    国货当自强,同情分不要也罢。

    国产深度学习框架的建设者,藏好后退的发际线,在时代的噪音里,纵身一跃。

    2020年,国产深度学习框架井喷。

    3月20日,清华大学计图(Jittor)。

    3月25日,旷视科技天元(MegEngine) 。

    3月28日,华为MindSpore。

    7月31日,一流科技OneFlow。

    四家国产,同期开源。五家国产,旌旗列阵。这一年最有可能被追认为国产深度学习框架的“元年”。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    守旧的经验是,既然国外开源了,就抓紧学。既然人家成了事实工业标准,就尽力参与。总是慢了好几拍,Linux这轮就是这样。

    引用某游戏厂商的经典台词是:“别催了,在抄了,在抄了。”

    可惜竞争从来不是游戏。

    深度学习框架的台词是:“不能照抄,不能舔狗,舔到最后,一无所有。”

    2020年,国产框架在技术上不是单纯的跟随者角色了,也有很多创新点可圈可点。

    飞桨作为国内最早的开源框架,模型库是最丰富的。以模型库的形式沉淀成深度学习框架生态的一部分,生态也起步早。

    古人云:“不谋全局者,不足以谋一域” 。有匿名采访者认为:“华为是国内投入框架研发最坚定的大公司。”

    可以观察到,华为剑指全栈AI战略,投入非常大。硬件算子库、基础软件、平台、产业基金、联合项目、标准、论文专利、人才,几乎所有的地方都发狠力。

    华为内部技术高管(笔者被反复要求匿名)告诉笔者:“大厂发展深度学习框架一定不是为了卖钱,而为了发展生态。华为发展深度学习框架,一方面是自主可控,一方面是坚定地发展AI全栈能力。Mindspore并没有拘泥于自家的芯片,不能仅仅视为一款产品,而是战略级的平台,这是明确公开说的。”

    翻看所有的宣传稿件,不难总结出,华为有全场景,端边云协同,比如,华为自己有手机业务,方便对硬件做指令级优化。

    但是,华为做的远不止这些。

    第一,在拿MindSpore 为抓手,来解决深度学习之外的、以前在超算领域关注的一些计算任务(科学计算)。其它框架虽然也有这个目标,但华为想到了,也做到了。

    第二,AI有个公开的槽点,即被黑盒问题所累。然而,牵扯到AI安全的问题,既基础,又前沿,搞得人少,困难多。对于基础软件来说,又格外重要。

    华为金雪锋博士有一个表述:“按DARPA(美国国防部先进研究项目局)的说法,可解释AI的目的,就是要解决用户面对模型黑盒遇到的问题,从而实现:用户知道AI系统为什么这样做,也知道AI系统为什么不这样做,用户知道AI系统为什么做错了。

    这个问题被华为关注,无疑提高了国产框架段位。你在研究拳法,我在研究拳法背后的哲学根基。

    华为MindSpore开源后,很多质疑的声音消失了,酝酿了半天的道德制高点没有骂出来,憋得怪难受。

    不过,有些批评,华为确实该虚心接受,不是外人,都能过去。

     

    3如何竞争?

    滔滔江水,浪奔浪涌,摩尔定律却日渐消失于地表。

    需要在硬件层面对AI进行优化浮出水面,因为在微观层面的编译器优化,需要和硬件厂商合作。这是华为的独家优势。在所有框架公司里,唯独华为有芯片。

    官宣用语:“用昇腾+MindSpore,构建华为数字底座”。

    华为被特朗普轰炸了几轮,印象十分深刻。

    在独家硬件的加持下,MindSpore的名场面是,有开发者感受到“快到飞起”的兴奋。

    这也不是唯一的路,因为深度学习编译器也登上了舞台。巧不巧,这又是一个底层技术。

    所以说,深度学习框架门槛高不可攀,算法、底层硬件、操作系统、分布式系统、编译器,一个都不能少。

    TVM编译器在2017 年开源,能够在任何硬件后端上有效优化和运行计算,可作为框架的后端。学术方面,进展也迅速,比如“如何利用TVM直接参与硬件设计过程的迭代,使得加速器设计在一开始的时候就可以直接获得软件的支持和目标的具体反馈”。

    TVM的背后是陈天奇团队,与其竞争的还是中国人民的老朋友,谷歌(MLIR)。

    国产框架,万箭齐发之势。

    创业公司代表队唯一的队员,一流科技袁进辉博士则放出豪言:“要做出世界上速度最快的。”

    AI科学家的豪言壮语,比起罗永浩的那句“收购不可避免走向衰落的苹果公司,并复兴它”,也没克制。

    天下武功唯快不破。

    他认为,第一,在分布式深度学习里,计算仅仅是一个方面,多个GPU上任务的协同需要频繁地把数据在GPU之间传来传去。数据在数据流图里行走(flow),想走得快,算得快,吞吐量得大,得将数据通信也设计成数据流图的一部分,不能让传输成了瓶颈。

    第二,哪里需要数据通信,需要什么形式的数据来进行通信,都要开发者去编程实现,这很麻烦,框架应该自动实现。

    袁进辉博士的总结是:“OneFlow有两个创新点:一会自动安排数据通信。二把数据通信和计算的关系协调好,让整体效率更高。”

    2020年,多节点和多设备的训练成为深度学习的主流,这一趋势符合袁进辉创业之初的判断,而这一思路可追溯到2014年他在微软亚研院的思考。

    袁进辉团队的短板明显存在,AI研发投入“壕无人性”,直白一点:创业公司穷。不过,2021年春节前,高瓴创投独家领投一流科技A轮融资,总额5000万元人民币。

    框架,A面是各有特色,B面是什么呢?

    答案是,大规模。

    这世界上唯一能够碾压国内一线城市房价增速的,只有AI模型的规模。虽然硬件和软件的进步已经将每年的训练成本降低了37%;但是,AI模型越来越大,以每年10倍的速度增长。

    人工智能模型就像宇宙飞船飞向太空最远处,正在探索能力的边界,拓展人类的想象力。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    大模型,跑步前进,工业级实现,拔腿直追。

    迈入大型模型训练时代,要求深度学习框架能够在面临数百台、数千台计算机的庞大规模时,有效地进行训练。

    比如,对于单个设备或多个设备数据并行这种简单场景的支持已经足够优秀,但在模型更大或者神经网络拓扑更复杂时,通用框架的易用性和效率都大打折扣,有这种需求的工业级应用只好下血本研发定制方案。

    大规模训练是当前各厂商竞争的一个焦点,谁输谁赢仍有变数。但可以肯定的是,只待“百团大战”的第一枪打响后,就是全方位的比拼(易用性,完备性,高效性)。

    坏消息是,国产在市场和生态上与美国巨头依然有很大的距离。

    好消息是,这不是一个完全被动的局面。

    甚至,国产框架的竞争也在细分,分化出局部战役。

    框架分为训练和推理两部分,训练框架难度大,推理框架次之。

    华为推理框架已经做到了生产级别,交付到了华为手机上。在手机巨头厂商中,框架的玩法,各不相同。

    都知道,苹果机器学习框架CoreML的代码是高度商业秘密。

    巨头的动作出其不意地整齐划一,端侧深度学习推理框架,BAT已经全部出手。

    百度Paddle Lite、阿里巴巴mnn、腾讯ncnn、华为移动端推理框架Bolt(华为诺亚方舟实验室开源)、OPEN AI LAB的边缘AI推理框架Tengine ,甚至连小米也有,MACE。

    单论技术难度,这些同类产品比深度学习框架低很多,但也各怀绝技,各有千秋,只是误放在一起比来比去,就不是内味儿了。

    深度学习框架的战场上,全行业最拔尖的团队悉数上场。

     

    4开源也竞争

    做基础软件,

    一要决心,

    二要耐心,

    三要开源,

    因为是大投入、长周期、抢生态。

    关于开源与生态,笔者最想采访的是美国硅谷创投圈资深人士,思科云计算事业部研发老大徐皞。

    多次联系,终于得到他的回复。

    他告诉笔者:“生态系统对操作系统而言,比操作系统本身更重要更难发展。这个道理很简单:操作系统可以雇几百个人写出来,生态需要恳求几万、几十万、几百万的人去写应用才算数。对手机电脑而言,多数用户是为应用买单,而不是为操作系统买单;对框架而言,多数用户是为能不能快速解决商业问题而买单。”

    开源是一个隐秘的角落,“大教堂与集市”的比喻口口相告,代代相传,是开发者眼中独一无二的圣地,挤满了来自全世界贡献与分享的热情,胸前佩戴“开源项目主要贡献者”的奖章,是江湖地位的象征。

    曾几何时,开源软件是对抗大公司的侠者。

    而如今,大公司却对开源软件越来越青睐。巨头对开源的投入,其背后是生态,是为了占领市场。开源软件的开发,不再是开发者之间松散的合作。

    开源软件公司有更多主导,开源软件的开发效率和质量都有所提升。

    开源的“不竞争”是另一种形式的竞争。眼下这几年,开源商业模式有变。徐皞认为:“开源软件真正兴盛,真正有突破,也就是五到十年的事情,开源软件商业模式依然在非常早期。”

    开源软件的背后是竞争,是研发与工程的投入,不投入,怎么占领。

    Linux是有很多家的贡献,但是安卓代码1200万行,全部是谷歌工程师自己写的。

    看看美国公司对开源市场的投入力度,中国公司不能落后,更应该主动投入,占据,甚至主导。

    开源和闭源,隔山两相望,且看那密密麻麻的布防,哪个山头都有重兵。

    开源软件世界里,框架虽为一隅,却极尽奇观。最好的思想,最好的代码都悉数拿出来了。这是分享,也是一种较量。

    前美国国防部咨询顾问,史蒂夫·马奎斯的说法是:“开源项目,来源于最纯粹的竞争。如果一个开源项目在商业世界获得了成功,那决不会是出于侥幸,决不会是因为其它竞争者恰好被规章制度所累、被知识产权法约束、被人傻钱多的金主拖垮。一个开源项目胜出了,背后只会有一个原因——它真的比其他竞争者都要好。”

    有借有还,再借不难。“借用思路”是爽了,但又诱发更深层次的竞争。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    上帝说,要有光。

    特斯拉说,要有电。

    开源说,要有代码。

    若问深度学习框架将带来什么,得想清楚深度学习的未来在哪。

    听说过深度学习又被称为软件2.0吗?作为数据驱动范式的顶峰,从数据里自动推导出程序,而不是必须靠程序员绞尽脑汁手动书写程序,这是一个划时代的进步。

    深度学习可能从一个小小岛屿,演进成一个大陆板块。

    在接下来的十年,深度学习软件有机会变成每个软件工程师医药箱里的必备“药丸”(不要乱想,不是蓝色的那种)。人类最重要的计算机软件将由其创造,自动驾驶,药物发现……

    开源软件的玩法自由奔放,但也有公地悲剧、PR铜臭。深度学习框架是一款理解成本很高的软件,群众基础薄(mei)弱(you)。于是,有人用“AI平台”一词,胡乱指代,张冠李戴,故意混淆,真令人作呕……大过节的,算了算了。

    有决心,就有私心,有疯子,就有骗子。

    时间总能给出答案。

     

    5结语

    古人云,按经济学的规律办事。

    大约两百多年前,英国经济学家杰文斯指出,技术成本降低,将提升技术的普及度,从而扩大市场规模。

    起初,戴着大粗金链子,说错了,戴着领结的大英煤老板十分担心,掐指一算:第一次工业革命让蒸汽机效率提升,每台用煤量减少,总的用煤量会下降,生意要下滑。

    结果事实正相反,用煤量大幅增加,好开心呀,因为蒸汽机使用成本降低了,使得蒸汽机用得更广泛了。

    框架的道理也一样,降低了研发人力成本,降低了计算资源成本,带动市场规模扩大。

    两百年后的今天,人工智能深度学习算法的大火,创造了算法软件包史无前例的机会,软件开发中的标准化就是把每个人都要干的活统一起来,成为工业化的环节。

    深度学习框架牛就牛在把共性提炼抽象出来,用最简约的代码实现,代码越简单越牛。

    软件流水线提升整个行业的水平,彻底替代手工打造的落后局面。

    搞深度学习框架的那帮人,不是疯子,就是骗子

    图为:贾扬清在阿里巴巴公司的工位

    搞深度学习框架的那群人,

    他们,可能是同学同事同行,亦狂亦侠亦友。

    他们,必然是浩宇璀璨群星,风雷意气峥嵘。

    贾扬清,化身修罗,重回故里,现任阿里巴巴技术副总裁。

    陈天奇,学府道场,CMU大学教书,投入深度学习编译TVM。

    李沐,蒲团打坐,驻守美国亚马逊,现任资深主任科学家。

    徐伟,开山老祖,现任地平线AI首席科学家。

    王益,绝顶神僧,谷歌、腾讯、蚂蚁金服美研主任科学家,2021年初去脸书公司。

    袁进辉,苦炼金刚,网名老师木,清华博后,微软科学家,穷酸创业。

    林敏,羽化成仙,跳出三界,研究基础理论去了。

    无论是产品,还是生态,最终,市场会决定胜出者。

    人工智能头顶高科技花环,被高高捧起,又被左右开弓扇耳光,灵魂三逼问:到底行不行?啥时候突破?谁杀死那只独角兽?

    突破难规划,创新难计划,独角兽不拼命也不行……此后,深度学习框架,对于国外开发者同样重要。

    需要发问的是:如何才能做出全球大流行的开源深度学习框架?网友质问的原话是:“你敢超过吗?”

    (未完待续,这篇文章太长了,转发后,去你的收藏夹吃灰吧。)

    展开全文
  • 继承关系 单继承: 一个子类只能由一个直接父类时称这个继承关系为单继承 多继承: 一个子类有两个或以上直接父类时称这个继承关系为多继承 菱形继承: 菱形继承是多继承的一种特殊情况 菱形继承问题: 从下面的对象...

    继承

    继承:保证原有类的基础上进行扩展,是类设计层次的复用

    1.class 子类名称:基础方式(public,protected,private) 父类(基类)

    2.继承中private和protected的区别: 在类里面private和protected几乎没什么区别,但是在继承里面二者有一定的区别
    在继承中,private继承的都不可见,但是protected方式继承的都可以看见

    1.基类的private成员在派生类中无论以什么方式继承都是不可见的,这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都是不能去访问它

    2.基类private成员在派生类中是不能被访问,如果基类成员不想再类外直接被访问,但需要在派生类中能访问,就定义为protected,可以看出保护成员限定夫是因为继承才出现的

    3.基类的其他成员在子类的访问方式=min(成员在基类中的访问限定符,继承方式)

    4.class 默认继承方式是private,struct默认继承方式位publuc

    1.C++中继承方式中private和protected的区别:

    如果在父类成员是protected,子类以protected/public方式继承,那么在子类中该成员为protected,就是父类中的成员在子类中可以进行访问,而在子类之外就不能进行访问

    //父类中定义年龄,姓名都是保护成员,那么在子类中就可以访问该成员,
    class Person
    {
    public:
    	friend class Student;
    protected:
    	string name = "Peter";
    	int age = 18;
    };
    class Student :protected Person
    {
    public:
    	
    	void Print()
    	{
    		cout << "Name=" <<Person::name<< endl;
    		cout << "Age=" << age << endl;
    		cout << "id=" << id << endl;
    	}
    	int id;
    };
    int main()
    {
    	Student s;
    	//s.Person::Print();
    	s.id = 20101320;
    	s.Print();
    	return 0;
    }
    

    image-20210924110903146

    如上所示,如果父类中是protected成员,那么在子类中可以访问父类的protected,而在子类外不能进行访问

    如果父类的成员访问权限为private,那么子类无论以何种方式继承了,子类都没有权限访问父类中的private成员,例:

    //我们将父类中的成员变量属性设置为私有,然后子类以共有的方式继承父类,看看子类可不可以访问父类
    class Person
    {
    public:
    private:
    	string name = "Peter";
    	int age = 18;
    };
    class Student :public Person
    {
    public:
    	
    	void Print()
    	{
    		cout << "Name=" <<Person::name<< endl;
    		cout << "Age=" << age << endl;
    		cout << "id=" << id << endl;
    	}
    	int id;
    };
    int main()
    {
    	Student s;
    	//s.Person::Print();
    	s.id = 20101320;
    	s.Print();
    	system("Pause");
    	return 0;
    }
    
    

    image-20210924111525816

    很明显,在继承中,子类无论以何种方式继承都不可访问子类中的private内容,若要访问,可以使用友元进行访问

    类成员/继承方式public继承protected继承private继承
    基类的public成员派生类的public成员派生类的protected成员派生类的private成员
    基类的protected成员派生类的public成员派生类的protected成员派生类的private成员
    基类的private成员派生类中不可见(派生类不可访问)派生类中不可见派生类中不可见

    2.赋值

    派生类对象可以将派生类的对象/派生类的引用/派生类的指针给父类,这里有个形象的说法叫做贴片或者切割,寓意把派生类中父类那部分切来赋值过去
    父类对象不可以赋值给派生类
    image-20210924144649252

    //将派生类的值复制给父类
    //为了方便观看,我们将所有的属性设置为共有
    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	string name = "Peter";
    	int age = 18;
    };
    class Student :public Person
    {
    public:
    	int id;
    };
    int main()
    {
    	Student s;
    	s.name = "Hello";
    	s.age = 10;
    	s.id = 20101320;
    	Person p;
    	p = s;
    	return 0;
    }
    

    如上所示,我们手动初始化Stduent创建出来的变量s,然后将s赋值给Person类型的p,这个时候什么来看一下效果

    image-20210924113620602

    //将派生类的应用,指针赋值给父类
    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	string name = "Peter";
    	int age = 18;
    };
    class Student :public Person
    {
    public:
    	int id=0;
    };
    int main()
    {
    	Student s;
    	s.name = "Hello";
    	s.age = 10;
    	s.id = 20101320;
    	Person p;
    	p = s;//将派生类的值赋值给父类
    	Person* prt = &s;//父类的指针可以指向派生类
    	Person& inference = p;//引用
    
    	return 0;
    }
    

    image-20210924141501642

    基类的指针可以透过强制类型转化赋值给派生类的指针,大师必须是基类的指针是指向派生类对象时才是安全的

    3.继承中的作用域

    1.在继承体系中基类和派生类都有独立的作用域

    2.子类和父类中有同名成员,子类成员将屏蔽父亲对同名成员的直接访问,这种情况叫做影藏,也叫做重定义(在子类成员函数中,可以使用基类::成员函数
    来显示访问)

    3.需要注意的是如果是成员函数的隐藏,只需要函数名相同就可以构成隐藏

    4.在实际继承体系中最好不要定义同名的成员

    例如:

    class Person
    {
    public:
    	int _age = 15;
    };
    class Student :public Person
    {
    public:
    	int _age = 25;
    };
    

    在父类和子类中定义两个名称相同的变量,默认访问的是子类的_age,如果想要访问父类的__age,就要加上作用域

    image-20210924150448390
    image-20210924150519829

    如果有重名的函数也是相同的道理:

    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	void fun()
    	{
    		cout << "Person::fun" << endl;
    	}
    };
    class Student :public Person
    {
    public:
    	void fun()
    	{
    		cout << "Student::fun" << endl;
    	}
    };
    int main()
    {
    	Student s;
    	s.fun();
    	s.Person::fun();
    
    	return 0;
    }
    

    image-20210924151207828

    4.派生类的默认构造函数

    6个默认构造函数,默认的意思是我们不写,编译器会为我们默认生成一个,在派生类中,这几个成员函数是如何完成的呢

    1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员,如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用

    2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化

    3.派生类的operator=必须要调用基类的operator=完成基类的复制

    4.派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员,因为这样才能保证派生类对象先清理派生类成员在清理基类成员的顺序

    5.派生类对象初始化调用基类构造再调用派生类构造

    6.派生类对象析构,清理先调用派生类析构再调用基类的析构

    例:

    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	//构造函数
    	Person(string _name="",int _age=0):
    		name(_name),
    		age(_age)
    	{
    		cout << "Person构造函数" << endl;
    	}
    
    	//拷贝构造函数
    	Person(const Person& s):
    		name(s.name),
    		age(s.age)
    	{
    		cout << "Person拷贝构造函数" << endl;
    	}
    
    	//赋值构造函数
    	Person& operator=(const Person& s)
    	{
    		if (this != &s)
    		{
    			name = s.name;
    			age = s.age;
    		}
    		cout << "Person=重载" << endl;
    		return *this;
    	}
    
    	//析构函数
    	~Person()
    	{
    		cout << "~Person析构函数" << endl;
    	}
    
    protected:
    	string name;
    	int age;
    };
    class Student :public Person
    {
    public:
    	//构造函数
    	Student(string name="",int age=0,int id=0):
    		Person(name,age),
    		_id(id)
    	{
    		cout << "Student构造函数" << endl;
    	}
    	
    	//拷贝构造函数
    	Student(const Student& p):
    		Person(p),
    		_id(p._id)
    	{
    		cout << "Student拷贝构造函数" << endl;
    	}
    
    	//=重载
    	Student& operator=(const Student& s)
    	{
    		if (this != &s)
    		{
    			Person::operator=(s);
    			_id = s._id;
    		}
    		cout << "Student=重载" << endl;
    		return *this;
    	}
    	//析构函数
    	~Student()
    	{
    		cout << "~Student析构函数" << endl;
    	}
    private:
    	int _id;
    };
    int main()
    {
    	Student s("校长", 26, 20101320);
    	Student t(s);
    	Student u;
    	u = s;
    	return 0;
    }
    

    image-20210924152711860

    5.继承与友元 友元关系不可以被继承 基类友元不能访问子类私有和保护成员

    #include<iostream>
    using namespace std;
    class Student;
    class Person
    {
    	friend void Print(Person& p, Student& s);
    public:
    protected:
    	int num=10;
    };
    class Student :public Person
    {
    public:
    protected:
    	int _num = 20;
    };
    void Print(Person& p, Student& s)
    {
    	cout <<p.num << endl;
    	cout << s._num << endl;
    }
    int main()
    {
    	Person p;
    	Student s;
    	Print(p, s);
    
    return 0;
    
    }
    

    image-20210924155508600

    上述代码我们在父类中定义void Print(Person& p, Student&
    s);该函数为友元函数,在子类中进行继承,发现该函数只能访问父类中的成员,而子类中的私有成员访问不了,因此友元不可以进行传递,若要使该程序正常运行,就需要在子类中也定义友元函数,例如,把上面的代码改成下面这样

    image-20210924160530140

    6.继承与静态成员变量

    继承与静态成员 静态成员被继承后所有成员共享,同时占有一个空间

    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	Person()
    	{
    		count++;
    	}
    	static int count;//静态成员变量在类内进行声明,类外进行初始化
    
    };
    int Person::count = 0;
    class Student :public Person
    {
    public:
    
    };
    int main()
    {
    	Student s1;
    	Student s2;
    	Student s3;
    	Student s4;
    	cout << Person::count << endl;
    
    	return 0;
    }
    

    image-20210924161640211

    7.继承关系

    单继承:

    一个子类只能由一个直接父类时称这个继承关系为单继承

    多继承:

    一个子类有两个或以上直接父类时称这个继承关系为多继承

    菱形继承:

    菱形继承是多继承的一种特殊情况

    image-20210926154644865

    菱形继承问题:

    从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性问题

    //如下所示,我们写了一个菱形继承,Student继承了Person,Teacher也继承了Person,然后Assistant继承了Student和Person
    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	string _name;
    };
    class Student :public Person
    {
    protected:
    	int  _num;
    };
    class Teacher :public Person
    {
    protected:
    	int _id;
    };
    class Assitstant :public Student, public Teacher
    {
    protected:
    	string _majorCourse;
    };
    int main()
    {
    	Assitstant a;
    	a._name = "hello";
    	return 0;
    }
    

    上述代码中Assistant继承了Student和Teacher,而这两个类又分别继承了Person,这就导致了Assistant拥有了两个Person的数据段,出现了二义性
    image-20210926155112558
    可以通过指明作用域改变上述问题,但是这又会产生数据冗余

    image-20210926155359213

    虚拟继承可以解决菱形继承的二义性和数据冗余的问题,如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题,需要注意的是,虚拟继承不能在其它地方去使用

    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	string _name;
    };
    class Student :virtual public Person
    {
    protected:
    	int  _num;
    };
    class Teacher :virtual public Person
    {
    protected:
    	int _id;
    };
    class Assitstant :public Student, public Teacher
    {
    protected:
    	string _majorCourse;
    };
    int main()
    {
    	Assitstant a;
    	a._name = "Peter";
    
    	return 0;
    }
    

    image-20210926155636811

    决菱形继承的二义性和数据冗余的问题,如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题,需要注意的是,虚拟继承不能在其它地方去使用

    #include<iostream>
    using namespace std;
    class Person
    {
    public:
    	string _name;
    };
    class Student :virtual public Person
    {
    protected:
    	int  _num;
    };
    class Teacher :virtual public Person
    {
    protected:
    	int _id;
    };
    class Assitstant :public Student, public Teacher
    {
    protected:
    	string _majorCourse;
    };
    int main()
    {
    	Assitstant a;
    	a._name = "Peter";
    
    	return 0;
    }
    

    image-20210926155636811

    展开全文
  • 面试官:webpack原理都不会

    千次阅读 多人点赞 2021-01-08 20:57:56
    ) 先完成“造轮子”前 30%的代码 项目起来了,但似乎还少点东西~~ 对了!基础的文件我们需要先完善下:forstpack.config.js和src。 首先是forstpack.config.js: const path = require("path"); module.exports...
  • mysql表关系

    千次阅读 2021-01-19 23:33:19
    表与表之间的关系"""把所有数据都存放于一张表的弊端1.组织结构不清晰2.浪费硬盘空间3.扩展性极差"""#上述的弊端产生原因类似于把代码全部写在一个py文件中,你应该怎么做?>>>解耦合!将上述一张表拆成...
  • } } 总结 Balking模式和Guarded Suspension模式从实现上看似乎没有多大的关系,Balking模式只需要用互斥锁就能解决,而Guarded Suspension模式则要用到管程这种高级的并发原语;但是从应用的角度来看,它们解决的都...
  • 三分钟明白Java内存分布,再也不怕了!!!

    千次阅读 多人点赞 2021-06-02 11:41:54
    内存是Java开发中不太关注的,但是是线上出问题最多的,三分钟明白Java内存分布
  • 玩技术还要搞关系,感觉自己又回到了山东 为了满足老板软硬兼施、不肯罢休还带着一点期待的眼神 我就说,那就让她写个日志分析查询的服务吧 心想写个查询的Java WebService,你能弄出什么幺蛾子 为了给她一点指导,...
  • 过去不会关系,今天就让我们来消除这份恐惧,微笑着勇敢的面对它吧! 所以小林整理了关于 TCP 的面试题型,全文共 3 万字 + 100 张图,跟大家一起探讨探讨。 1、 TCP 基本认识 2、TCP 连接建立 3、 TCP 连接断开...
  • 拷贝与Java内存结构息息相关,懂Java深浅拷贝是很必要的! 在对象的拷贝中,很多初学者可能不清到底是拷贝了引用还是拷贝了对象。在拷贝中这里就分为引用拷贝、浅拷贝、深拷贝进行讲述。 引用拷贝 引用拷贝会...
  • 来源 | 沉默王二责编 | Carol头图 | CSDN 下载自 视觉中国之前在 CSDN 上分享过一篇文章,涉及到 Java 中的注释,就信誓旦旦地写了一句话:“注释是不会被执行的!”结果,有小伙伴留言说,“老师,你确定吗?”我这...
  • 无为而治,自然而然,要成长为新的物种,就要经历所有你不会再扮演的角色。 明白了吧? 元宇宙讲究的就是去中心化,无为而治,最少的规则发挥出最大的能量,所有的事情并非都能计划好,我们只需要将自然界中所有的...
  • } } 结语 这篇到这里就肝完啦,其实快速幂的内容还不止这么多,尤其是矩阵快速幂,会有着各种巧妙的变形,不过跟数学有一些关系,在力扣上比较少,但是在其他OJ上快速幂题目还是很多的可以自行找一下刷一刷。...
  • 在长期运行时,因为我们有乘法关系,所以这个比值可能要么会爆炸,要么就会消失。这对估计的目的而言是有一点问题的。 3).加权重要度采样 为了降低方差,降低估计的幅度是一种简单又直观的方法,具体做法是除以所有...
  • 独热码 模型训练通常是不会接收object 或 string型,只能接收数值型,但独热码能将分类变量的object 或 string型转换为定量变量的数值型。 Ø 函数:get_dummies Ø 举例:‘fuel’ 有gas和dissel两类,通过pd.get_...
  • 历史不会重演,但总会押韵。2006年宝时捷收购大众造成了史诗级轧空事件,在十五年后的今天再围绕游戏驿站上演,只是对决的双方由变成抱团的散户与传统的空头机构。 而社交媒体的推波助澜使这场对局的达到了空前...
  • 所以,即使有个牛逼的黑客破解了某一次通信过程的私钥,其他通信过程的私钥仍然是安全的,因为每个通信过程的私钥都是没有任何关系的,都是独立的,这样就保证了「前向安全」。 ECDHE 算法 DHE 算法由于计算性能不...
  • 原标题:错综复杂的人际关系,一张图谱一目了然 小白最近有点烦心事,她是一所公办学校的老师,在外面办培训班被人举报了。教育局三令五申,禁止教师在校外办各种辅导班或者培训班,这件事情把校长也连带地弄得...
  • 一文明白ARP

    2021-05-21 11:36:21
    比以太网帧的最小帧长少了4 bytes,原因是因为wireshark抓包时不能抓到数据包最后的CRC字段 (3)抓包看看 请求包 响应包 5、ARP缓存表 计算机中会维护一个ARP缓存表,这个表记录着IP地址与MAC地址的映射关系 ARP表...
  • vue.js基础还不会?——看这篇文章就够了

    千次阅读 多人点赞 2021-04-30 17:59:04
    插值出的内容不会继续更新】 6、v-model v-model是一个指令,限制在 、components中使用 双向数据绑定: 当数据发生变化的时候,视图也就发生变化 当视图发生变化的时候,视图也会跟着同步变化 7、v-on 用来绑定事件...
  • 而是与发动机的转速有直接的关系,也就是说跟你脚下的油门有着直接的关系,一般说来,在一定范围内,发动机转速越高,它输出的功率(动力)就越大、扭矩就越大,相应的车速就会越来越快。功率的极限数值决定车速最高能...
  • 经常会遇到一些ldquo;...”的问题,如果想彻底懂原因,需要研究下Oracle的字符集以及他们之间的转换的关系,还有OS字符集、NLS_LANG跟数据库字符集的关系。简单的总结下要懂的几个要点:1. NLS_...
  • 再谈开发人员和测试人员的关系发表于:2011-12-30来源:未知作者:娃娃点击数:开发人员和测试...你会不会用软件呀?再谈测试人员的关系开发人员和测试人员的主要矛盾就集中在对bug的定义上。测试人员辛辛苦苦发现软...
  • 复制代码代码如下: A B CSS z-index 属性顺序规则的例子 定位规则 如果将 position 设为 static, 位于文档流后面的节点依然会遮盖前面的节点浮动, 所以 position:static 不会影响节点的遮盖关系. 复制代码代码如下:...
  • 数据库系统概论——关系数据库关系数据库一、关系数据库的结构和形式化定义(一)、关系(二)、关系模式(三)、关系数据库,关系模型的存储结构二、关系操作三、关系的完整性(一)、实体完整性(二)、参照完整性...
  • 本文主要描述了关系型数据库设计的各个阶段及重要概念,并重点介绍了 概念设计 和 逻辑设计 两大核心阶段,着重强调了 E-R 模型的构造 步骤,除此之外还补充了 关系模式的规范化 及如何 求解关系模式的候选码 等重要...
  • 数量关系

    2021-03-08 09:57:32
    数量关系数量关系1数量关系2数量关系3数量关系4数量关系5数量关系6(1)数量关系7(2)数量关系8(3)数量关系9(2.21)数量关系10(2.24)数量关系11(3.6) 数量关系1 解析: 设本月销售额为x,上月销售额为y,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 233,291
精华内容 93,316
关键字:

不会搞关系