Caffe 作者贾扬清:我为什么离开 Google,加入 Facebook?

Caffe 是全球最普遍使用的图像识别开源软件,贾扬清是它的作者。贾扬清说,希望Caffe成为深度学习领域的Hadoop。过去贾扬清一直在 Google Brain 工作,也是 TensorFlow 深度学习引擎的共同作者,不过他最近改变了职业发展的方向,来到Facebook 继续人工智能的研究。我们来看看贾扬清的故事。

文/新智元(微信号:AI_era)编译 作者:周建丁 编辑:王嘉俊 王婉婷
来源:知乎、程序员、LinkedIn、Google Scholar

贾扬清:Caffe 作者,现任Facebook研究科学家,曾在Google Brain工作。在AI领域有数年的研究经历。在UC Berkeley获得计算机科学博士学位,在清华大学获得硕士和本科学位。对两款流行的深度学习框架做过贡献:Caffe的作者,TensorFlow的作者之一。

工作经历:2016年2月从Google离职,加入Facebook,致力于前沿AI研究和平台开发。2013年12月到2016年2月在Google Brain担任研究科学家,致力于前沿的深度学习研究和工程,参与了ImgeNet2014比赛、移动端深度学习、Google下一代AI平台TensorFlow开发、基于深度学习的产品开发和产品咨询等。

贾扬清离职了?

最早接触贾扬清,是在雷鸣师兄的群里。那时候雷鸣组织了一场线上分享会,贾扬清给大家分享了Caffe背后的故事以及相关的实现算法。虽然之前一直有所耳闻,但大神的讲座还是刷新了印象:逻辑太清晰了,而且真的非常年少有为。

在这之后一直有关注贾扬清的消息。今天查看 LinkedIn 的时候,突然发现贾扬清的工作已经变成了 Facebook。嗯?他不是一直在 Google Brain 么?

在 LinkedIn 工作栏上写着:从 2016 年 2 月开始,在 Facebook 担任科学家,从事前沿的人工智能研究和平台开发。

鉴于贾扬清是知乎活跃分子,我们又查了他的知乎情况。果然,2月20日,知乎上有一个帖子被热烈讨论:如何评价caffe作者贾扬清加入Facebook?

贾扬清自己回复说:

“正常换工作而已,大家不需要太过关注。。。

就我个人而言,在Google实习过两年又工作过两年,无论是技术还是科研都感觉收获颇丰,换到Facebook的原因也是为了在个人发展上能学到一些不同的东西,为将来的职业发展继续做准备。另外一个原因是好多以前伯克利同实验室的朋友也在Facebook,比如Ross Girshick和Bharath Hariharan,所以也增加了一份亲切感。

两家都是好公司,也都是牛人云集,所以从找工作的角度说,来哪儿都不会让你感觉后悔的。

不过话说我哪儿都没有announce到底是谁捅出来的消息呢?”

知乎评论里不少人对贾扬清加入 Facebook 有很多解读,有人猜想:谷歌估计铁了心让Tensor Flow一统江湖。Caffe 到了脸书至少可以和Torch分庭抗礼。

还有人评论说:感觉torch要发迹了?再不济应该torch支持caffe或者caffe支持torch?反正不管怎么说作为torch用户看到大家去facebook我还是心欢喜的。

我们来看看贾扬清的研究工作

在 Google Scholar 上,贾扬清被引用最多的三篇文章:

1.Caffe: Convolutional architecture for fast feature embedding(引用1201次)
2.Going deeper with convolutions(引用660次)
3.Decaf: A deep convolutional activation feature for generic visual recognition(引用562次)

在研究工作上,贾扬清之前的工作中,可公开的是:

How Google Translate squeezes deep learning onto a phone。

当然,他也是 Google TensorFlow 的作者之一。

新智元联系上了贾扬清本人,他确认了加入 Facebook 的消息。不过由于 Facebook 政策的原因,采访需要得到公司的许可,所以我们没能马上对此展开采访工作。不过贾扬清“补偿”了一张当年的相片,非常帅气。

新智元祝贺贾扬清,新工作顺利!

新智元正在跟进对贾扬清的采访工作,敬请关注后续发展。今天我们推荐一篇文章,是《程序员》杂志2015年7月对贾扬清的专访,新智元已获授权转载如下。

贾扬清:希望Caffe成为深度学习领域的Hadoop

本文来源:《程序员》 | CSDN | “人工智能头条”公众号(微信号:AI_Thinker)
作者:周建丁(zhoujd@csdn.net)

摘要:在深度学习的热潮下,Caffe作为一个高效、实用的深度学习框架受到了广泛的关注。《程序员》记者近日深度对话Caffe作者贾扬清,剖析Caffe的起源、目标、差异性、现存的一些问题和改进工作,以及未来的规划。

在深度学习(Deep Learning)的热潮下,Caffe作为一个高效、实用的深度学习框架受到了广泛的关注。了解Caffe研发的背景、愿景、技术特色、路线图及其开发者的理念,对于我们选择合适的工具更好地进行深度学习应用的迭代开发大有裨益。《程序员》记者近日深度对话Caffe作者贾扬清,剖析Caffe的起源、目标、差异性、现存的一些问题和改进工作,以及未来的规划。

起源故事

《程序员》:请介绍一下您自己与深度学习结缘的故事,以及开发Caffe的背景和初衷?

贾扬清:我经常和人开玩笑说,“我写Caffe的原因是因为我不想写毕业论文。”

我最早开始接触深度学习大概是在2012年,主要是做sparse coding以及相关的特征学习。在2013年的时候,我和伯克利心理学系的Thomas Griffith教授开始合作研究这样一个问题:我们人类在个人成长过程中是如何形成“类别”这样的概念。我们发表的文章提出了一个很好的概率框架来表达人的行为(参见我在NIPS 2013的文章),但是因为图像上提取的特征比较弱,所以可以外推的结果比较有限。2013年的时候,因为Alex Krizhevsky在ImageNet的成功,我开始考虑把CNN的特征用到心理学的这个实验上,于是就写了Decaf。Decaf需要基于cuda-convnet来训练,但是我们通过Decaf验证了深度学习特征的优异的可移植性(参见我们在ICML的Decaf文章)。这个应该算是我开始考虑实现一个完整的深度学习框架的初衷。

然后在2013年下半年的时候,NVIDIA的学术捐赠计划送了我一块K20 GPU,我当时正好在写毕业论文,空闲之余就准备把这个计划实现出来(对一个研究生而言,GPU很贵的)。我从9月下旬开始,先自己攒了一个机器,然后大概花了两个多月的时间写了整个架构和ImageNet所需要的各个实现。起初纯粹是因为兴趣使然作为一个业余的项目,后来因为越来越觉得有意思(写代码可能和玩游戏上瘾差不多),花在Caffe上的时间逐渐变成20%、40%、80%,后来在上下班的地铁上也开始编程序,毕业论文倒是没有太重视,所幸我的导师开明,也没有说什么。Caffe写完以后在我们组里面试用,大家都觉得它挺好使 – 比如Jeff和Ross利用Caffe的训练代码实现了R-CNN。到了11月份的时候,我开始考虑是不是要开源Caffe,然后12月份正式开源。顺便一提,我的毕业论文最后是第二年5月份才写完的。

在接下来的一年里面Caffe开始吸引很多其他的用户和开发人员,特别是NVIDIA开始帮助我们做更多的加速,Berkeley也成立了Berkeley Learning and Vision Center来组织和吸引工业界的研究人员共同开发多个开源项目(包括Caffe)。我毕业以后还在继续和伯克利的同事开发Caffe,同时在Google继续搞深度学习的应用和研究;我们在伯克利也建立了一个核心的Caffe团队:Evan Shelhamer开始用他以前在开源社区的经验来牵头多方面的合作;Jeff Donahue在业余时间还帮助Pinterest建立了他们的深度学习系统;Jonathan Long给Caffe提供了例如Python接口等等最新的特性。而且,Caffe开始吸引世界各地的人来尝试和使用深度学习的技术,这个是我们所始料未及的。

哦,对了,因为Caffe的缘故,最开始的心理学项目反而被我搁下了,而后也是诸事繁杂,希望有一天我还能够继续回去完成那个项目。

《程序员》:在Caffe的开发过程中,哪些事情让您印象深刻?克服的最大挑战是什么?

贾扬清:让我印象最深刻的事情是,写Caffe其实占用了我写毕业论文的时间,我最开始向我导师保证说十二月底可以写完论文(因为年底就要开始工作了),结果半路跑去写Caffe了,拖了好长时间的进度。有一次我和我导师开会说我个人的担心,然后他问我“你是想多花时间写一个大家估计不是很在意的毕业论文呢,还是多花时间写一个将来大家都会用的系统?”我觉得博士生多年,我导师教我最多的就是分清主次,这一点我获益良多。

另一个好玩的小事情是,我2013年在Google实习喝了太多的咖啡,起Decaf这个名字是为了督促自己把咖啡戒了。后来因为GPU速度快就起了Caffe这个名字。伯克利我们组的研究生都很喜欢Caffe,向我导师要经费买了一个冰滴咖啡的咖啡机,结果我们后来又天天喝Evan做的咖啡,戒不掉了。Caffe的核心开发人员在工作允许的范畴下还会做一些consulting的工作,邮箱名字也因此起了caffe-coldpress。

说到挑战的话,Caffe是我做的第一个从零开始,而且有实际工程意义的项目,而且是初学GPU,所以在编程上开始还是挺困难的。但是我觉得最大的挑战是决定应该以什么方式来公布Caffe:是成立一个创业公司,是作为一个纯科研不能商用的程序库,还是作为一个完全开源的系统。每个方向都有人偏好,而且平心而论,开源这条道路也许在经济上并不是最优的。我大概前后花了一个星期的时间来说服和我合作的每个人,甚至在一定时候需要用“这是我写的框架,所以我应该有决定权”这样的理由来解释。强迫别人接受自己的观点不是我的个性,所以我觉得很幸运最后大家都很支持这个决定。作出开源的决定应该是整个项目里面最挑战的一部分 – 写代码倒在其次了。

在Caffe之前,深度学习领域缺少一个完全公开所有的代码、算法和各种细节的框架,导致很多的研究人员和博士生(例如我)需要一次又一次重复实现相同的算法,这不好。我觉得作为一个科研人员,还是需要有开放的胸怀来帮助整个社区的发展,所谓不忘初心,方得始终。反正咖啡还是买得起的,夫复何求呢。

社区生态及问题探讨

《程序员》:作为一个开源工具,Caffe的社区建设和代码管理情况如何?

贾扬清:Caffe目前主要还是一个伯克利主导,然后由github加上caffe-users邮件组所组成的一个比较松散和自由的社区。因为主要的贡献者都还是学生(包括已毕业的学生),而且大家主要的工作也还在科研上面,所以社区主要是依靠Caffe的使用者来自发组成的。我们也在摸索一些新的方法,比如说邀请工业界熟悉Caffe,贡献过Caffe的代码的朋友来参与管理Caffe,或者组成一个简单的committee/foundation来帮助代码管理等等。实话说,Caffe在社区建设代码管理上还是有很多可以进一步提高的地方,这方面我们也在继续摸索。

《程序员》:经常有人说,好的开源工具不等于好的产品。我了解的Caffe的安装和配置还比较复杂,有开发者评价说Caffe文档好,上手方便。但那个巨大的protobuf配置文件就是个灾难,而且改动代码很麻烦。对于这个问题,有什么解决办法?或者说使用Caffe还需要一定的门槛?

贾扬清:Caffe的安装和配置的确是一个比较让人头疼的问题,特别是如果不熟悉Linux或者不在Linux的系统上的话,安装Caffe可能需要不少的一段时间。最近我们也在社区的帮助上面做一些改进,主要是:

NVIDIA在帮助我们将Caffe放到apt-get系统里面,使得将来可以直接apt-get install caffe;

我们在完善各个layer的文档,使得大家更容易地实现一些自己的算法;

在protobuf上,我们加入了Python Wrapper,可以借助简单的Python函数来定义网络,而不是写一个巨大的protobuf(GoogLeNet如果手写有两千多行… 的确有点发指)。

我在简化一些Caffe的依赖关系,使得我们不需要编译所有的库(比如说leveldb,HDF5等等)也可以使用Caffe,或者可以根据自己需要选择相应地库文件来编译。

总的来说,因为是个C++的框架,所以使用Caffe还是有一定的门槛,如果大家遇到问题,也可以在caffe-users@googlegroups.com上面发信交流。

《程序员》:另外有一位CSDN博主之前写了Caffe使用体验,谈到套用原有模型很方便,但个性化就要读源代码,比较痛苦;Theano只是提供计算函数,搭Network的细节都是自己定,非常灵活,但速度不如Caffe。针对目前的深度学习框架,您主要关注哪些?您认为他们各自的优劣如何?开发者应该如何选择?

贾扬清:我读过那篇文章,的确,Caffe在文档上相对还比较痛苦一些。这个主要是因为我们更多的定位在科研上面,假设说大家都会有一定的时间和精力来读代码。目前我们主要能做的是尽量多地公开一些样例和教程,使得大家在各种应用上都能比较容易地找到和需求比较相似的例子。在文档方面,我们也在增加一些代码当中的文档和注释,但是因为我们主要的开发人员还是在校的学生,所以重心还是放在科研上。大家可以看见Caffe team最近开创性的工作很多,包括了classification,detection,segmentation,以及最近用LSTM来实现图像描述这样的工作等等。如果开源社区愿意帮助提高现在的文档水平的话,我们感激不尽:)

Theano和Caffe的定位稍微有点不同:Theano主要定位在更加容易地做一些快速的研究和迭代上,所以实现的方法很多时候是基于Python以及底层的C转译的。这样做的好处是可以更加容易地修改代码,但是优化上就不那么容易。我的感觉是,可以采用Python这样的语言来做很快的迭代,但是等到确定一个具体的算法以后,可能还是需要转成C++/CUDA的代码来达到比较好的速度。从这一点上,Caffe提供了Python Layer来帮助做一些prototype,应该会有帮助。

《程序员》:您长期在做CV方面的工作,能否透露Caffe在您的工作中的使用情况和实际效果?在基于Caffe的深度学习方面有什么通用的优化技巧可以分享?

贾扬清:Caffe最开始的确就是为了在我们平时科研上使用所写的,所以在伯克利我们所有涉及到Deep Learning的工作也都会基于Caffe来进行。我在Google也同时会使用Google Brain的大规模的机器学习框架DistBelief(参见Google在NIPS 2012的文章),但是我会经常使用Caffe来做一些快速的prototype和实验,因为Caffe有着更小的系统框架,使得一些探索性的实验更加容易一些。值得一提的是,Google的研究人员经常会使用各种自己熟悉的开源框架来进行小规模的研究,然后用DistBelief来作为一个通用的框架实现大规模产品级别的部署。

说到通用的优化技巧,我的经验就是从简单的实现开始,寻找到瓶颈以后再做具体的优化。一个比较直接的例子是Caffe的卷积层:我当时使用了一个将它转化为矩阵乘积的方法来进行计算,虽然相对比较耗费内存,但是在实际应用中一度是最快的实现。后来因为卷积在Deep Learning当中的重要性,大家纷纷开始实现更加有效的算法(比如说cuDNN),解决了最初实现中的种种问题。

另外,我个人觉得在研究生阶段,特别是机器学习的研究生阶段,学一点实际的编程语言(比如C++和Python)是很有帮助的,我以前大部分程序都在Matlab下实现,但是改用C++和Python以后,发现Matlab下很难实现的很多优化,比如说多GPU,多个机器的通信等等,可以非常容易地实现出来,于是就不再用Matlab了(听起来有点广告嫌疑…)。

《程序员》:我们通常采用Linux+Caffe,但也有很多人在Windows下跑Caffe,我看到一个介绍,说Windows版本的Caffe比Linux版本还要快,整合cuDNN速度就更快了,是否存在这样的情况?

贾扬清:我个人对此持谨慎的怀疑态度,因为主要的程序都是C++编写的,所以应该在速度上没有什么差别,看到的不同可能只是因为在两个平台上编译器优化不同的结果。因为编译器的不同优化选项会影响到速度,所以我一般提及速度的比较的时候会比较谨慎。一般而言,只要底层的硬件相同,像Caffe这样主要是数学计算的系统,速度应该是大同小异的。当然,也有可能Windows上的GPU驱动做的比Linux好,所以使得在GPU上运行速度比较快,这个是有可能的,但是我个人没有太多经验(我一般只用Linux和Mac)。

《程序员》:深度学习框架一般都整合CUDA、cuDNN,我看到NVIDIA最新推出的DIGITS率先支持Caffe,这需要Caffe进行一些特别的工作吗?另外Caffe社区与NVIDIA开源团队如何互动?

贾扬清:NVIDIA对于Caffe的支持非常好,这一点我们整个Caffe team也非常感谢。基本上,除了和NVIDIA的开发人员讨论一些实现细节之外,我们没有对DIGITS做任何特殊的改动。从一定的方面来说,这也说明整个框架很容易移植。NVIDIA对Caffe的支持主要在两方面:软件上提供CuDNN的整合和优化,以及硬件上对于科研的支持。我们Caffe的核心成员定期会和NVIDIA的团队开会讨论双方的需求和遇到的问题,以及新版本发布的时候的测试、集成的流程。总的来说合作非常愉快,而且也获得了不错的成果。

《程序员》:大规模的并行在深度学习中的作用很重要,能否介绍Caffe并行版,包括单机多卡并行和多机并行的Roadmap和当前进度?

贾扬清:Caffe并行的实现应该很快就会整合进来,因为其实实现本身并不困难:我最近几次报告都提到了基于MPI的实现方法。目前在伯克利我们有一个基于自己的机群所编写的并行实现,Yahoo/Flickr在github上也有一个并行实现的pull request。什么时候并入caffe的master branch其实主要是人手的问题,大家都在忙于实现新的算法,没有太多精力处理代码库的问题… 我们在Berkeley目前也在寻求一位PostDoc来负责Caffe的日常维护和团队管理,在这里顺便做个广告。

《程序员》:知乎上有一个问题:“做一个基于C++的物体识别系统,Caffe只提供了Matlab和Python的接口的,C++要怎么使用Caffe?希望能提供具体的配置(Linux下)和调用方法。”能否介绍Caffe在语言支持的选择方面是如何考虑的?对于广大的C++用户,您有什么建议?

贾扬清:Caffe主要的代码其实都是C++的,所以C++比Python和Matlab更加容易集成一些。当然,因为C++本身的缘故,所以实现一些数据的预处理比Matlab和Python要更困难一些。对于C++的话,可能就是多读Caffe的源代码吧:) 另外Caffe的tutorial一般都有对应的样例代码,顺着这些样例找到相应的实现,可能比单纯从底层读代码会更容易一些。

未来展望

《程序员》:与深度学习的发展和应用相比,您对Caffe目前的整体开发进度的满意度如何?Caffe未来需要解决哪些问题、增加哪些特性或者做哪些方面的优化,能否从整体上介绍一下未来的研发计划?

贾扬清:Caffe整体的开发进度还是非常令我感到惊讶的,特别是考虑到Caffe的核心成员只有五个人 – 我,Evan Shelhamer,Jon Long,Jeff Donahue和Sergio Guadarrama。每个人都有自己日常的科研和工程任务,但是同时我们还很好地保持了Caffe的维护和改进工作。最近我们正在做的工作主要是整合github上的各个pull request,包括多GPU计算、Python支持等等。

我觉得我们在CVPR 2015上的tutorial基本上概括了Caffe最近或者正在增加的特性:http://tutorial.caffe.berkeleyvision.org/。当然,我们一如既往地公开了所有的细节,包括源程序和训练好的模型。目前我个人除了科研以外主要在做的是两方面的内容,一方面是继续开源Google最近在Deep Learning上的科研成果(比如说我参与的GoogLeNet),另一方面是重新设计Caffe的一些结构,使得它更模块化,更容易在各种环境下部署。

《程序员》:现在很多云服务商都提供了机器学习云服务,Caffe基于云服务的优化是如何规划的?

贾扬清:据我所知,目前很多机器学习的云服务都是基于简单的模型,比如说线性的Logistic Regression等等来提供的。最近有许多公司开始提供更加复杂的机器学习服务,比如说Microsoft Project Oxford等等,也许将来我们就可以很容易地使用深度学习的最新成果了。对于Caffe而言,我们更多地注重在深度学习的研究上面,所以对于云服务的优化并不是一个主要的方向。当然,我觉得Caffe的开发团队非常优秀,所以将来也许会走一条和工业界更加紧密联系的道路。《程序员》:是否因为架构或者设计的原因,使得Caffe会存在一些天生不擅长的深度学习场景?Caffe训练出来的模型和其他的框架产生的模型如何很好支持地转化?

Caffe一开始主要是为了图像领域的应用所设计的,所以在非图像的问题,比如说一般的机器学习、语音识别等问题上,Caffe的框架并不一定是最优的。举个例子,我们最近对于Caffe的数据结构做了一些修改,但是那之前Caffe的数据都需要强制定义成四维的Tensor:显然,在很多应用当中这是不够灵活的。根据我们从开发者社区得到的反馈,在很多非图像的应用领域,包括语音、自然语言处理、金融数据处理等等,其实也有很多人在使用Caffe。我们最近也在继续修改Caffe的框架,希望在不同领域的应用能够更加方便。

关于模型在不同框架之间转化的问题,我觉得我的回答是“与其用多个框架,不如优化一个框架” – 客观地说,Caffe在通用性和可移植性上比其他的框架(比如说基于Python的Theano和基于Lua的Torch)要灵活很多。Caffe的社区里面也有爱好者提供不同框架之间的转换代码,如果的确需要的话,可以搜索一下。

《程序员》:目前大型互联网公司的深度学习系统都依赖于GPU加速,当然Caffe也支持无GPU模式,您如何看待非GPU加速的深度学习的前景?比如在FPGA加速或者未来基于神经网络芯片的系统,Caffe会如何支持?

贾扬清:非GPU加速的深度学习目前而言还是有一定的优势的,因为如果计算GPU的成本,目前云端服务的价格还是很贵 – 这主要是因为GPU本身带来更多成本,比如前期投入以及后期的维护等等。GPU的更新换代也会导致投入和产出的平衡更加难以计算。如果深度学习被证明有着巨大的产品需求的话,我觉得FPGA以及ASIC这样的专用芯片会逐渐出现并且占据市场 – 毕竟在固定算法的前提下,它们在速度上和成本上都有着巨大的优势。

在Caffe的支持上,我个人并不是非常担心,因为深度学习的模型都可以用比较通用的数学函数表达出来。举个例子,在没有GPU的时候,深度学习的算法都是在CPU上实现的;在GPU出现以后,模型还是一样的模型,但是速度得到了一个数量级的提高。我觉得FPGA也会一样,只要有芯片的产品以后,由Caffe(或者其他深度学习的框架)所训练的模型只需要相应的实现代码就可以重现。当然,软硬件的同步开发也是需要的。

《程序员》:业界已经有人尝试深度学习在智能设备上的应用,芯片厂商在研究嵌入式视觉SoC,您自己也尝试过Raspberry Pi和Jetson,您如何看待嵌入式系统深度学习的趋势?在这方面Caffe还能够做些什么?

贾扬清:嵌入式的深度学习应该是一个大的趋势 – 就像百度IDL的创始人余凯博士说的一样,“世界终归是属于做机器人的人”。从广义上来说,各种智能设备都可以归为机器人的范畴。目前而言深度学习的大部分算法还是基于云端的计算,但是随着最近几年计算能力飞速提高,很多算法已经可以在嵌入式芯片(比如说NVIDIA的Tegra K1)上实时地运行了。想象一下,如果我们身边的每一个设备,从汽车到冰箱都是智能的,这该是多么美好的一件事情!这样的想法其实很早以前就有了(我记得我读初中的时候就听说过微软的智能家居的计划),但是我觉得现在我们站在一个技术的拐点上,也许可以真正实现以前的这些梦想。

从一定意义上说,希望深度学习在所有平台上都可以运行,这也是我最开始写Caffe的希望,也是为什么我选了C++而不是其他的语言。我目前正在修改一些Caffe的架构,希望能够更加容易地实现模块化的设计:核心Caffe代码是最小化和完全可移植的,然后可以根据需求来增加各种其他的模块。希望这样一来,Caffe的应用都能很高效地自定义到各种平台上面。

《程序员》:最后,请谈谈您对Caffe在深度学习领域的未来有什么期待?

贾扬清:用一句话来说,我希望Caffe能够成为机器学习和深度学习领域的Hadoop:人人了解,人人使用,人人获益。目前而言,深度学习领域的发展日新月异,各种框架发展非常迅猛,但是一定程度上也导致很多具体实现的碎片化。我个人的希望是通过不断改进Caffe本身的框架、代码、优化和周边各种支持,将Caffe变成一个业界标准的框架,提供一个相对普适的平台,让大家能更好地交流科研的成果,以及更快地将这些成果转化到实际的应用中去。

·氧分子网(http://www.yangfenzi.com)延伸阅读:

您可能还喜欢…

发表评论

邮箱地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>