COS访谈第十八期:陈天奇

【COS编辑部按】 受访者:陈天奇      采访者:何通   编辑:王小宁

简介:陈天奇,华盛顿大学计算机系博士生,研究方向为大规模机器学习。他曾获得KDD CUP 2012 Track 1第一名,并开发了SVDFeature,XGBoost,cxxnet等著名机器学习工具,是Distributed (Deep) Machine Learning Common的发起人之一。

何:你的本科在上海交大的ACM班就读,是怎么开始做机器学习研究的呢?

陈:我们当时的培养计划里面有一项,就是希望我们尽早地接触学术研究。于是我们在大二暑假就要开始进实验室了,在大三的暑假去微软亚研(MSRA)实习,于是我大二暑假去的是俞勇老师的实验室,当时戴文渊学长也在交大做迁移学习这一块的研究,所以我就跟着他了,也就是这个时候开始接触的机器学习。不过后面其实换了很多方向,因为戴文渊其实带了我半个学期就毕业了。后来我到微软实习做的是和广告相关的东西。之后实验室的一位老师建议我的毕设做深度学习。当时是2010年吧,深度学习还没有完全火起来的时候,整体的趋势是大家都在做无监督学习。当时实验室正好有一块比较老的显卡,我就开始写一些CUDA的程序。从毕设一直到研究生一年级我一直都在做无监督深度学习,也尝试过ImageNet,但是没有得到很好的结果。后来正好有KDD Cup这个机会,之后我们就逐渐往推荐系统的方向做了,因为2011和2012年的题目以推荐系统为主。

何:你在刚接触深度学习的时候就开始写CUDA程序,是一开始就想写机器学习里面的各种工具了吗?

陈:一开始我们在CUDA上面写的是Restricted Boltzmann Machine(RBM),后来这一套代码和其中的思想就逐渐演化成为现在的mshadowcxxnet了。这个项目本身没有特别成功,但是有一套思想保留了下来,就是写机器学习代码应该以矩阵运算为主,而不是一味的写for循环,哪怕你是一个C++程序员(笑)。一开始在做这个项目的时候我是一个追求性能的C++程序员,就是我会用inline去优化所有东西,但是后来发现代码很不可读。接着我就把代码重构了一遍,把矩阵运算的相关部分抽了出来。不过当时对Expression Template不太熟,于是我就写了一套类似的机制,但是没有现在的mshadow那么成熟。当时我们做了一个系统,相当于现在mshadow的雏形,可以去做一些矩阵的操作,并且在GPU上面跑。后来我寒假的时候到爱尔兰交换,当时他们在做KDD Cup,正好RBM也可以用到推荐系统当中,于是我就想尝试一下,结果最后效果不是特别好。不过有了这次尝试之后,我在研一时也决定和实验室的几个同学参加KDD Cup 2011。当时我们就想到可以做一些基于特征的矩阵分解,就是factorization machine这一套,当时我们就开发了一个工具,不过并不完善。后来结合我们在深度学习上面的经验,就做出了SVDFeature。KDD Cup是一个比较大的平台,我们希望能做一个比较强大的工具,从而尝试各种各样的想法。而且当时还有一些比较苛刻的要求:比如我们实验室的Hadoop机器是32位的(笑),每台机器是4核8G的内存,我们如果想用机器去做分布式调参,那么每个模型只能用2G的内存。在这样的环境下,我们必须在SVDFeature中增加外存计算(编者:将数据储存在硬盘上,每次读入适量数据进入内存的方法)的功能,这也是我真正意义上的第一个外存计算的项目。

何:一般来说,我们做一个工具的时候不会在一开始就去考虑增加外存计算的功能。

陈:是的,不过当时是我们的确需要,而且实现起来并不困难,因为矩阵分解本身就是一个在线的算法。我们的SVDFeature使用了之前的矩阵运算代码,这个工具我自己整个研究生生涯都在使用。这让我认识到非常重要的一点:写的代码要清楚,要写矩阵运算的代码,要用内联和模板去优化这个代码,而且后来我发现用矩阵运算的代码比我自己另写的代码在编程速度和运行速度上反而都要快一点(笑)。SVDFeature是一个非常Hacky的项目,我们要能定制推荐系统中各种各样我们能想到的模型,比如可以添加用户特征,物品特征,全局特征,我们还实现了特殊的数据结构把数据安装用户分组从而提高效率。这个东西很高效,到现在还有人在使用。但是对用户来说并不是特别好用,比如你需要知道并且能转换出数据的正确输入格式,然后再去SVDFeature里面写一个很长的配置文件去读它(笑)。我们作为开发者,用这个工具去参加了两次KDD Cup,拿了第三名和第一名。我们组里面也有人用这个工具做研究,发了一些比较好的文章。所以它的自定义性很强大,适合用来做研究。我们在第二次比赛之前就开始往SVDFeature里加Gradient Boosting算法,所以可以说XGBoost的前身出现在了SVDFeature里。

何:你们拿KDD Cup第一名的比赛用的是12年的腾讯微博数据?

陈:对,那次比赛的题目是预测腾讯微博里的关注请求,提供的信息有社交网络的信息,以及用户的一些个人信息。因为我们有SVDFeature,所以就先拿来做了一遍矩阵分解。但是后来发现一些连续的信息用矩阵分解来处理并不是最好的,比如用户的年龄以及发微博次数等变量。于是我们就想通过树模型来利用这样的信息,所以最后我们的模型是矩阵分解和树模型的一个叠加:在矩阵分解的结果上用树模型进行优化,所以我们在SVDFeature里加上了树模型。 当时是每个人都有一个树模型,然后每个物品都会通过这棵树进行分类。后来我第一个发到ICML的工作就是在此基础上的一个扩展,就是把每个物品通过矩阵分解进行分类,然后然后在每个类别里面构建一棵树就行了。这篇文章是我在华为实习的时候与李航老师和杨强老师合作的。

何:你刚刚也提到了工具会对研究有帮助,对么?

陈:其实我做工具的目的就是希望它能对我的研究有帮助。我还有一篇论文是条件随机场和树模型的结合。所以你可以看到我需要有这么一个建立树模型的工具。做XGBoost的原因是做实验的时候发现效率太低了,因为原来SVDFeature里面的模型是单线程的。我在快从交大毕业时做的一个项目里接触到了OpenMP,发现非常好用。所以我发现目前为止最适合写多线程的语言不是Python不是Java,反而是有了OpenMP的C++。至少机器学习里的很多算法直接用它就可以解决了。于是我就想能不能利用它写一个多线程的GBDT(Gradient Boosting Decision Trees)帮助我做研究。我给这个工具起名extreme gradient boosting,因为我自己比较喜欢把一件事情推到极限,就像当初开发SVDFeature时我们要利用2G内存训练一个模型一样。我刚到华盛顿大学的时候就在业余时间开始写XGBoost,它的第一个版本是在14年的寒假做出来的。之后我做了一些比较,发现效率挺高的,就发布了出来,不过当时是没有python接口的,只有C++接口。因为我那时的习惯仍然是类似于SVDFeature一样有非常高的自定义性,从而支持我的研究。之后我对第一个版本做了一次代码重构,正好碰上Kaggle的希格斯子竞赛开始,第二天我就拿去跑了一遍,拿了当时排行榜的第一(笑)。之后许斌问我是不是可以把这个作为benchmark公开,并且建议我做一个python的接口。

何:python和R涵盖了几乎所有kaggle用户的选择。

陈:是的,于是我就去学习了一下python里一个叫做ctypes的功能,它可以直接调用C的代码,于是我花了两天时间写了一个python的接口,并在比赛的论坛里发布了出来。到这时我开始改变了自己的想法:因为你不能让用户为了使用一个C++程序而去写非常复杂的配置文件,只能尽让他们设置最关键的参数。这个工具发布到了论坛之后,就有越来越多的人来使用,python接口也越来越完善。到最后我发现我自己都不愿意去用C++接口了(笑)。在希格斯子比赛的时候,许斌还以这个工具拿了另一个比赛的第四名。那个暑假我去graphlab实习,于是我就把XGBoost放进了graphlab里面,就是它里面的BoostedTreesClassifier模型。后来我又对代码进行了第二次设计上的重构,把单个的树模型和对树的控制分得很清楚,在这个版本的基础上我又觉得应该加上R的接口(笑),因为似乎很多人也想用R。于是我就学习了怎么在R里调用C,并做了个比较简单的R的脚本。接下来就找了你做这个XGBoost的R包。这个过程我觉得和做SVDFeature有一个非常大的差别:以前我认为一个工具基本能用就可以了,需要最大化自定义性来方便研究,而作为一个用户我反而应该着重于它的易用性,让用户调控最少的参数,让他们能更方便地使用这个工具。所以我也在开始想把SVDFeature也做一个这样的工具出来,不过未来可能会有我的一位合作者来完成这件事。

何:这是你在XGBoost基本成型之后的下一个计划吗?

陈:这应该算是dmlc的一个组件,但是我不会主要负责它的开发。因为每个人都会有自己感兴趣的方面,我研究生阶段着重于矩阵分解这方面的研究,然后接下来我想去做一些我不会的东西,而且我的这位合作者应该是这个项目比较合适的人选。

何:因为我参与了XGBoost的开发,所以我也了解你平时花在上面的时间很多,那么你是怎么平衡开发工具与做研究的时间的呢?

陈:我的这些工具最终都会用到研究里面。因为我的研究方向是大规模机器学习,所以最近我的一部分精力放到了dmlc的核心组件上,比如分布式文件读写,分布式的调度等项目。很多这些项目都是做分布式机器学习和系统研究的先决条件,这些项目包括XGBoost最后都会运用我的研究当中。

何:所以花时间打磨这些工具对你的研究是有帮助的。

陈:对的,不过这取决于研究的方向,比如我愿意去探究如何将boosted tree应用在大规模数据集上。的确,我在写这些工具上也花了一些时间,特别是在即将发布重要版本更新的时候:在发布python和R接口时,XGBoost逐渐成型;分布式版本发布后,XGBoost能支持各种各样比较好的平台,这也和我的研究方向有关,现在它的分布式版本用到rabit库就是我在做的另一个项目。分布式机器学习也需要有一些基础的库来支撑:文件读入,通信等。在做这些项目的时候,我也会去思考怎么划分整个系统,哪些部分放到通信里,哪些部分放到模型里,这些思考也会有帮助。我也不会把所有的时间都投入进去,因为每年我会选很多的课,也要有上课的时间,所以就是每周花晚上的时间来做这些工具。

何:是每天晚上吧?

陈:有时候是每天晚上(笑)。花时间不少,但是我觉得做的事情还是很有趣的。其实去年我做的三个主要项目是mshadow,XGBoost和cxxnet。而mshadow和cxxnet是绑在一起的,大部分的雏形都是在去年暑假之前就做好了的,中间经历了几次比较大的重构。cxxnet和XGBoost是差不多同时发布的,不过我们最近拿它的2.0版本拿了个kaggle图像识别比赛的第二名。因为我以前尝试过深度学习但是没有出什么成果,有点遗憾,所以想再去试一试,就做出来了cxxnet。这里面比较有意思的成果是mshadow库,里面用到了很多c++的优化技术,使得你去控制你的资源,并且能够高效的计算。cxxnet以后也可能慢慢发展为一个比较好的深度学习库。我们也开始考虑做一些对用户友好的R或python的接口,现在已经有了python的接口但是还不够友好。其实dmlc里面有两个深度学习的项目,一个是cxxnet,一个是minerva。后面我们希望能够将这两个项目合在一起,做出一个比较简洁又比较高效的工具。

何:你经常会拿自己做的工具来参加比赛,那么这属于你试验自己工具的一种手段吗?

陈:算是吧,比如第一次在希格斯子比赛上拿出来遛遛,就是试验工具的一种手段。不过最近我基本不参加比赛了,因为参加比赛还是比较花时间的(笑)。上次cxxnet的比赛我其实没有花很多时间参与在里面,不过这次也是为了测试它的极限在哪,因为有一个东西总想拿出来跑一跑。有时候也不需要刷成绩,比如Michael写的那个变量重要性的功能,我觉得也很有意思,能让你对数据有更深刻的了解,可能比你直接把一个分数调到很高更有意义。

之后我会逐渐将精力往研究上靠,工具里面也许会出现一些我的研究课题,可能会有一些新功能但是不一定起效果(笑)。不过目前来看分布式训练等功能还是很有用的。

何:那么你觉得现在的XGBoost是在一个比较成熟的阶段了么?

陈:我觉得相对来说它确实到了一个比较成熟的阶段了,可以说该有的东西基本都有了。未来需要完善的功能应该是外存计算,这是最近刚加进去的功能。其实出现这个功能的原因也是很直接的:我当时在20台机器上面跑400G的数据做分布式XGBoost实验,于是每台机器就要分20G的原始数据,这些数据在内存里做一些数据结构的处理,可能就需要60G的内存,这就已经塞不下了。所以又想到了外部存储这个解决方法,加入了这个功能。

何:所以这和你SVDFeature碰到的瓶颈一模一样,殊途同归呀。

陈:是啊,都是一样的。开发工具总会在某个阶段碰到各种各样的问题,所以这也是为什么我希望把一个工具做到极限。即使内存这样的资源很大,也会出现类似的问题。随着计算架构的复杂,我们可以通过GPU来读本地缓存,那么这时候本地缓存就相当于一个只有100K的内存,所以这时候你还是要很小心地优化。所以我在dmlc里,更希望能够抽象出一些通用的库,让每个项目都可以共享。比如dmlc有一个稀疏矩阵的外存计算功能,比如你做矩阵分解的在线学习,直接用这个库就可以了,用的时候感觉不到这是外存计算,因为后端已经帮你做好了。另一个方面是线程流水线,比如你在处理HDFS数据的时候会用一个线程读数据,另一个线程做计算,上一块数据计算完成之后这个线程可以直接在一个缓冲区中用之前读好了的数据。所以这样的流水线让计算线程不需要考虑读取数据的问题。我们实现了这样的库,就能够让其他的工具设计起来更方便。第三个方面是文件的读写,我们在dmlc里面实现了这样的一个库,比如现在XGBoost能直接读写hdfs等文件系统,在分布式系统上的配置就方便。我们就希望总结出机器学习中这些通用的组件,然后将它们放在dmlc-core里,最后会让所有的其他库受益。

何:所以你现在更希望先把这些通用的组件开发出来,再在这上面开发新的工具?

陈:其实时间顺序是反过来的,我们一般没法在一开始就能把抽象的结构给总结出来。因为我们同时开发了XGBoost,cxxnet,以及之后还有开发矩阵分解工具的需求,当我们发现了这些工具中有共同的需求之后,再将它们给抽象出来。现在dmlc里面的东西都是为了这些工具服务,做分布式工具毕竟绕不开这些需求。

何:你之前在开发SVDFeature时着重于可塑,而到了开发XGBoost时则转变为重视易用性。那么未来的dmlc工具还会继续坚持易用性么?

陈:我觉得易用性是一定要保持的,而且易用性和可塑性在比较好的设计下也不是矛盾的。可能为了可塑性和效率会做一些很复杂的设计,这时候为了易用性就需要这个接口非常干净,这不是很容易的事情,不过我们至少要保证dmlc里面的工具有通用的配置文件这样互相调用就很方便。我希望保持易用性,这样才会有用户来使用。

何:写工具的目的也是希望有别的用户来用。

陈:对的,我最后也会去用比较易用的接口(笑)。我本来的想法是我不需要用python或者R,我只需要用c++读一个配置文件就行了,结果后来我也去用python了。

何:我之前也用XGBoost的python接口,自从写了R包之后就再也没用过python接口了(笑)。

陈:大家都比较懒嘛,而且这样的偷懒挺好的。

何:那么现在你觉得dmlc的这一系列工具在机器学习圈里属于什么样的水平呢?因为现在开源工具越来越多了。

陈:我喜欢做的一件事情是把一个工具推到极限。需要能懂系统设计的东西,也要懂机器学习的知识。我现在希望能把这些系统方面的知识抽象成标准库,然后让别人用的时候不需要了解系统级别的知识也能得到很好的优化。我们现在已经做了一些这样的事情,在这方面应该比现有的一些机器学习工具有一定的优势。然后在易用性方面反而是要向其他工具多多学习的,比如R里面的caret包,mlr包,以及python的scikit-learn。sklearn的接口看上去比XGBoost的更友好一些,不过XGBoost现在也支持从sklearn中调用了,这是最近有人加上去的,因为他们想用sklearn嘛,他们觉得sklearn的接口更方便一点。我现在更关心易用性,而一部分合作者可能会更希望在性能方面下功夫。另一个有优势的地方是dmlc对分布式的支持,我们希望能够在单机上把一个工具做到最好,然后开发分布式的版本。不过一个比较成熟的模型可能会直接开发分布式的版本。这和我的研究兴趣也相关,我希望探索一个工具在分布式环境下的表现能有多好,以及接口能做到多么易用。

何:你现在如何看待学术界与工业界中的机器学习研究与应用?

陈:因为我没有在工业界呆过,所以不太好评价这个问题(笑)。不过工业界有很多好的机会,包括非常丰富的数据资源。不过相对而言学术界的研究方向更自由。所以我认为很重要的一点是如何将学术界的研究成果很快地应用到工业界,并逐渐发展起来。比如当前的深度学习就是一个例子,XGBoost里面也用了一些新的技术。再比如矩阵分解,word2vec,他们都首先是学术界的成果。所以我们希望dmlc能作为一个桥梁,作为一个社区吸引更多有兴趣的人为此做贡献。这样这些工具能够比较快地在工业界常用的平台上应用,并且发展起来。

何:如果你未来去工业界工作,那么什么样的工作方向满足你的期许?

陈:我觉得设计一个新系统是个很有意思的事情,或者是不断地去做一些不一样的事情。人很多时候会陷入一个舒适区(comfort zone)里,比如我们做完SVDFeature之后其实可以考虑用它来发一堆论文,因为在这个领域有积累就会倾向于在这方面做下去。但是至少在读PhD或者未来更长的时间里,需要有机会来做一些不会做的事情。比如当初我们在SVDFeature上加入树模型,其实就是一个探索的过程。等XGBoost成熟之后,或许我也不会永远做树模型,可以去做一些其他的模型。就是尽量不要让自己陷入舒适区里,尝试一些自己以前不能够做的事情。

何:所以只要做的内容够有吸引力,在工业界或者学术界中工作对你来说并没有那么重要?

陈:其实他们还是有一些不一样的。在公司里会更关心业务,在学术界也会受到基金等因素的制约。我觉得总的来说,如果我能预测明年我在做什么事情,那就没有意思了(笑)。比如我刚开始做XGBoost的时候就没法预料到它会发展成今天这个样子,至少从工具的角度而言它比SVDFeature要成功很多。当然在整个过程中你是不断积累的,在XGBoost里面也有SVDFeature甚至更早的深度学习代码中的影子,这些经验会慢慢地体现在你的项目里。

何:你觉得dmlc以后会发展成像graphlab一样的工具集合么?

陈:我觉得应该不会。我觉得dmlc更像一个组成各个机器学习工具的组件集合。因为我们的初衷很简单,就是把每个人项目里面要做的事情抽出来减少工作量,比如参数服务器和分布式模型都需要做文件的读写。你会发现写C++的人很有意思,大家都认为自己是Hacker,喜欢从头开始造轮子(笑)。但是有时候这样并不好,一个人再强,他的精力也是有限的,不可能做到所有的东西。所以我们现在可以拿出更多的时间来做和自己的研究相关的工作。从更大的方面来说,既然C++有这么多hacker,是不是可以把大家聚集起来做一些更有意思的事情呢?所以我觉得它不会成为特别商业的东西。而且我们未来也会和包括Dato等公司有合作,他们现在也在用一些比如XGBoost和cxxnet等dmlc里面的工具。Graphlab有很多很独到的东西,比如它的data.frame是目前为止唯一一个利用外存计算把单机性能推到极致的dataframe产品,还有他们的机器学习流水线和很不错。我们的重点应该是把我们擅长的工具做到极限,完成各个核心组件,然后让它们和已有的其他工具和平台更好地整合。

COS访谈第十八期:陈天奇的更多相关文章

  1. 【算法•日更•第二十八期】图论:强连通+Tarjan算法(一)

    ▎前言 一直都想学习这个东西,以为很难,结果发现也不过如此. 只要会些图论的基础就可以了. ▎强连通 ☞『定义』 既然叫强连通,那么一定具有很强的连通性. 强连通:就是指在一个有向图中,两个顶点可以互 ...

  2. 【第十八期】分享一个网易go面经

    自我介绍 未来的主要方向 介绍下之前的项目用到的优化点.难点 为什么不要大量使用goroutine gpm模型 go里面goroutine创建数量有限制吗? 线程和协程有什么区别 golang支持哪些 ...

  3. Web 前端开发精华文章集锦(jQuery、HTML5、CSS3)【系列十八】

    <Web 前端开发精华文章推荐>2013年第六期(总第十八期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和 C ...

  4. 为什么不建议给MySQL设置Null值?《死磕MySQL系列 十八》

    大家好,我是咔咔 不期速成,日拱一卒 之前ElasticSearch系列文章中提到了如何处理空值,若为Null则会直接报错,因为在ElasticSearch中当字段值为null时.空数组.null值数 ...

  5. Web 前端开发人员和设计师必读文章推荐【系列二十八】

    <Web 前端开发精华文章推荐>2014年第7期(总第28期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  6. Python之路【第十八篇】:Web框架们

    Python之路[第十八篇]:Web框架们   Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Pytho ...

  7. 【OpenCV新手教程之十八】OpenCV仿射变换 &amp; SURF特征点描写叙述合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/33320997 作者:毛星云(浅墨)  ...

  8. 了解Kubernetes主体架构(二十八)

    前言 Kubernetes的教程一直在编写,目前已经初步完成了以下内容: 1)基础理论 2)使用Minikube部署本地Kubernetes集群 3)使用Kubeadm创建集群 接下来还会逐步完善本教 ...

  9. 使用Minikube部署本地Kubernetes集群(二十八)

    前言 使用Minikube部署本地k8s集群相对比较简单,非常推荐将其用于本地k8s开发环境,唯一麻烦点的仅仅是网络问题. 在本篇教程中,我们使用了国内的镜像来完成本地k8s集群的搭建.如果搭建过程中 ...

随机推荐

  1. ActionScript 3.0数组操作

    var arr:Array=new Array();arr=["a","b","c"];  //赋初值,注意这里的即使单个字符赋值使用的是& ...

  2. cocos2d_android开发简单游戏

    1)游戏图层设计: public class WellcomeLayer extends CCLayer { public WellcomeLayer() { this.setIsTouchEnabl ...

  3. Android 网络框架--Retrofit

    1.导入Jar包 compile 'com.google.code.gson:gson:2.8.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' c ...

  4. C primer plus 读书笔记第十二章

    C的强大功能之一在于它允许我们控制程序的细节.C的内存管理系统正是这种控制能力的例子.它通过让我们决定哪些函数知道哪些变量以及一个变量在程序中存在多长时间来实现这些控制. 1.存储类及其说明符 主要的 ...

  5. 辛星浅析跨域传输的CORS解决方式

    首先我们有一个概念.那就是"同源准则",也就是same-origin  policy,它要求一个站点(协议+主机+port号)来确定的脚本.XMLHttpRequest和Webso ...

  6. 如何判断MSSQL数据库磁盘出现了瓶颈

    问大神石沫:如何判断MSSQL数据库磁盘出现了瓶颈? 石沫(A1):您好,您的问题非常好,SQL SERVER提供了很多关于I/O压力的性能计数器,请选择性能计算器PhysicalDisk(Logic ...

  7. TCP 连接的要点

    概念 TIME_WAIT: socket 仍然有数据在内核中待发送直到发送成功或超时,此socket不能被内核删除,同时等待是否要重传Ack对端还已发过来的FIN Linger Time:socket ...

  8. 实例化讲解 RunLoop

    实例化讲解RunLoop 之前看过很多有关RunLoop的文章,其中要么是主要介绍RunLoop的基本概念,要么是主要讲解RunLoop的底层原理,很少用真正的实例来讲解RunLoop的,这其中有大部 ...

  9. 微信,QQ这类IM app怎么做——谈谈Websocket

    前言 关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作.直到最近换了工作,到了一家是含有IM社交聊天功能的app的时候,我觉得我现在可以谈谈 ...

  10. Android进阶笔记01:Android 网络请求库的比较及实战(一)

    在实际开发中,有的时候需要频繁的网络请求,而网络请求的方式很多,最常见的也就那么几个.本篇文章对常见的网络请求库进行一个总结. 一.使用HttpUrlConnection: 1. HttpUrlCon ...