Android单元测试研究与实践

处于高速迭代开发中的Android项目往往需要除黑盒测试外更加可靠的质量保障,这正是单元测试的用武之地。单元测试周期性对项目进行函数级别的测试,在良好的覆盖率下,能够持续维护代码逻辑,从而支持项目从容应对快速的版本更新。
单元测试是参与项目开发的工程师在项目代码之外建立的白盒测试工程,用于执行项目中的目标函数并验证其状态或者结果,其中,单元指的是测试的最小模块,通常指函数。如图1所示的绿色文件夹即是单元测试工程。这些代码能够检测目标代码的正确性,打包时单元测试的代码不会被编译进入APK中。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/f6b3c032.png)
图1 单元测试工程位置

与Java单元测试相同,Android单元测试也是维护代码逻辑的白盒工程,但由于Android运行环境的不同,Android单元测试的环境配置以及实施流程均有所不同。

Java单元测试

在传统Java单元测试中,我们需要针对每个函数进行设计单元测试用例。如图2便是一个典型的单元测试的用例。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/7f6b69db.png)
图2 单元测试示例

上述示例中,针对函数dosomething(Boolean param)的每个分支,我们都需要构造相应的参数并验证结果。单元测试的目标函数主要有三种: 1. 有明确的返回值,如上图的dosomething(Boolean param),做单元测试时,只需调用这个函数,然后验证函数的返回值是否符合预期结果。 2. 这个函数只改变其对象内部的一些属性或者状态,函数本身没有返回值,就验证它所改变的属性和状态。 3. 一些函数没有返回值,也没有直接改变哪个值的状态,这就需要验证其行为,比如点击事件。

既没有返回值,也没有改变状态,又没有触发行为的函数是不可测试的,在项目中不应该存在。当存在同时具备上述多种特性时,本文建议采用多个case来真对每一种特性逐一验证,或者采用一个case,逐一执行目标函数并验证其影响。
构造用例的原则是测试用例与函数一对一,实现条件覆盖与路径覆盖。Java单元测试中,良好的单元测试是需要保证所有函数执行正确的,即所有边界条件都验证过,一个用例只测一个函数,便于维护。在Android单元测试中,并不要求对所有函数都覆盖到,像Android SDK中的函数回调则不用测试。

Android单元测试

在Android中,单元测试的本质依旧是验证函数的功能,测试框架也是JUnit。在Java中,编写代码面对的只有类、对象、函数,编写单元测试时可以在测试工程中创建一个对象出来然后执行其函数进行测试,而在Android中,编写代码需要面对的是组件、控件、生命周期、异步任务、消息传递等,虽然本质是SDK主动执行了一些实例的函数,但创建一个Activity并不能让它执行到resume的状态,因此需要JUnit之外的框架支持。
当前主流的单元测试框架AndroidTest和Robolectric,前者需要运行在Android环境上,后者可以直接运行在JVM上,速度也更快,可以直接由Jenkins周期性执行,无需准备Android环境。因此我们的单元测试基于Robolectric。对于一些测试对象依赖度较高而需要解除依赖的场景,我们可以借助Mock框架。

Robolectric环境配置

Android单元测试依旧需要JUnit框架的支持,Robolectric只是提供了Android代码的运行环境。如果使用Robolectric 3.0,依赖配置如下:

testCompile 'junit:junit:4.10'
testCompile 'org.robolectric:robolectric:3.0'

Gradle对Robolectric 2.4的支持并不像3.0这样好,但Robolectric 2.4所有的测试框架均在一个包里,另外参考资料也比较丰富,作者更习惯使用2.4。如果使用Robolectric 2.4,则需要如下配置:

classpath 'org.robolectric:robolectric-gradle-plugin:0.14.+'//这行配置在buildscript的dependencies中
apply plugin: 'robolectric'
androidTestCompile 'org.robolectric:robolectric:2.4'

上述配置中,本文将testCompile写成androidTest,并且常见的Android工程的单元测试目录名称有test也有androidTest,这两种写法并没有功能上的差别,只是Android单元测试Test Artifact不同而已。Test Artifact如图3所示:

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/6e8678e9.png)
图3 Test Artifact

在Gradle插件中,这两种Artifact执行的Task还是有些区别的,但是并不影响单元测试的写法与效果。虽然可以主动配置单元测试的项目路径,本文依旧建议采用与Test Artifact对应的项目路径和配置写法。

Mock配置

如果要测试的目标对象依赖关系较多,需要解除依赖关系,以免测试用例过于复杂,用Robolectric的Shadow是个办法,但是推荐更加简单的Mock框架,比如Mockito,该框架可以模拟出对象来,而且本身提供了一些验证函数执行的功能。Mockito配置如下:

repositories {jcenter()
}
dependencies {testCompile "org.mockito:mockito-core:1.+"
}

Robolectric单元测试编写结构

单元测试代码写在项目的test(也可能是androidTest,该目录在项目中会呈浅绿色)目录下。单元测试也是一个标准的Java工程,以类为文件单位编写,执行的最小单位是函数,测试用例(以下简称case)是带有@Test注解的函数,单元测试里面带有case的类由Robolectric框架执行,需要为该类添加注解@RunWith(RobolectricTestRunner.class)。基于Robolectric的代码结构如下:

//省略一堆import
@RunWith(RobolectricTestRunner.class)
public class MainActivityTest {@Beforepublic void setUp() {//执行初始化的操作}@Testpublic void testCase() {//执行各种测试逻辑判断}
}

上述结构中,带有@Before注解的函数在该类实例化后,会立即执行,通常用于执行一些初始化的操作,比如构造网络请求和构造Activity。带有@test注解的是单元测试的case,由Robolectric执行,这些case本身也是函数,可以在其他函数中调用,因此,case也是可以复用的。每个case都是独立的,case不会互相影响,即便是相互调用也不会存在多线程干扰的问题。

常见Robolectric用法

Robolectric支持单元测试范围从Activity的跳转、Activity展示View(包括菜单)和Fragment到View的点击触摸以及事件响应,同时Robolectric也能测试Toast和Dialog。对于需要网络请求数据的测试,Robolectric可以模拟网络请求的response。对于一些Robolectric不能测试的对象,比如ConcurrentTask,可以通过自定义Shadow的方式现实测试。下面将着重介绍Robolectric的常见用法。 Robolectric 2.4模拟网络请求 由于商业App的多数Activity界面数据都是通过网络请求获取,因为网络请求是大多数App首要处理的模块,测试依赖网络数据的Activity时,可以在@Before标记的函数中准备网络数据,进行网络请求的模拟。准备网络请求的代码如下:

public void prepareHttpResponse(String filePath) throws IOException {String netData = FileUtils.readFileToString(FileUtils.toFile(getClass().getResource(filePath)), HTTP.UTF_8);Robolectric.setDefaultHttpResponse(200, netData);
}//代码适用于Robolectric 2.4,3.0需要注意网络请求的包的位置

由于Robolectric 2.4并不会发送网络请求,因此需要本地创建网络请求所返回的数据,上述函数的filePath便是本地数据的文件的路径,setDefaultHttpResponse()则创建了该请求的Response。上述函数执行后,单元测试工程便拥有了与本地数据数据对应的网络请求,在这个函数执行后展示的Activity便是有数据的Activity。
在Robolectric 3.0环境下,单元测试可以发真的请求,并且能够请求到数据,本文依旧建议采用mock的办法构造网络请求,而不要依赖网络环境。 Activity展示测试与跳转测试 创建网络请求后,便可以测试Activity了。测试代码如下:

@Test
public void testSampleActivity(){SampleActivity sampleActivity=Robolectric.buildActivity(SampleActivity.class).create().resume().get();assertNotNull(sampleActivity);assertEquals("Activity的标题", sampleActivity.getTitle());
}

Robolectric.buildActivity()用于构造Activity,create()函数执行后,该Activity会运行到onCreate周期,resume()则对应onResume周期。assertNotNull和assertEquals是JUnit中的断言,Robolectric只提供运行环境,逻辑判断还是需要依赖JUnit中的断言。
Activity跳转是Android开发的重要逻辑,其测试方法如下:

@Test
public void testActivityTurn(ActionBarActivity firstActivity, Class secondActivity) {Intent intent = new Intent(firstActivity.getApplicationContext(), secondActivity);assertEquals(intent, Robolectric.shadowOf(firstActivity).getNextStartedActivity());//3.0的API与2.4不同
}

Fragment展示与切换 Fragment是Activity的一部分,在Robolectric模拟执行Activity过程中,如果触发了被测试的代码中的Fragment添加逻辑,Fragment会被添加到Activity中。
需要注意Fragment出现的时机,如果目标Activity中的Fragment的添加是执行在onResume阶段,在Activity被Robolectric执行resume()阶段前,该Activity中并不会出现该Fragment。采用Robolectric主动添加Fragment的方法如下:

@Test
public void addfragment(Activity activity, int fragmentContent){FragmentTestUtil.startFragment(activity.getSupportFragmentManager().findFragmentById(fragmentContent));Fragment fragment = activity.getSupportFragmentManager().findFragmentById(fragmentContent);assertNotNull(fragment);
}

startFragment()函数的主体便是常用的添加fragment的代码。切换一个Fragment往往由Activity中的代码逻辑完成,需要Activity的引用。 控件的点击以及可视验证

@Test
public void testButtonClick(int buttonID){Button submitButton = (Button) activity.findViewById(buttonID);assertTrue(submitButton.isEnabled());submitButton.performClick();//验证控件的行为
}

对控件的点击验证是调用performClick(),然后断言验证其行为。对于ListView这类涉及到Adapter的控件的点击验证,写法如下:

//listView被展示之后
listView.performItemClick(listView.getAdapter().getView(position, null, null), 0, 0);

与button等控件稍有不同。 Dialog和Toast测试 测试Dialog和Toast的方法如下:

public void testDialog(){Dialog dialog = ShadowDialog.getLatestDialog();assertNotNull(dialog);
}
public void testToast(String toastContent){ShadowHandler.idleMainLooper();assertEquals(toastContent, ShadowToast.getTextOfLatestToast());
}

上述函数均需要在Dialog或Toast产生之后执行,能够测试Dialog和Toast是否弹出。

Shadow写法介绍

Robolectric的本质是在Java运行环境下,采用Shadow的方式对Android中的组件进行模拟测试,从而实现Android单元测试。对于一些Robolectirc暂不支持的组件,可以采用自定义Shadow的方式扩展Robolectric的功能。

@Implements(Point.class)
public class ShadowPoint {@RealObject private Point realPoint;...public void __constructor__(int x, int y) {realPoint.x = x;realPoint.y = y;}
}//样例来源于Robolectric官网

上述实例中,@Implements是声明Shadow的对象,@RealObject是获取一个Android 对象,constructor则是该Shadow的构造函数,Shadow还可以修改一些函数的功能,只需要在重载该函数的时候添加@Implementation,这种方式可以有效扩展Robolectric的功能。
Shadow是通过对真实的Android对象进行函数重载、初始化等方式对Android对象进行扩展,Shadow出来的对象的功能接近Android对象,可以看成是对Android对象一种修复。自定义的Shadow需要在config中声明,声明写法是@Config(shadows=ShadowPoint.class)。

Mock写法介绍

对于一些依赖关系复杂的测试对象,可以采用Mock框架解除依赖,常用的有Mockito。例如Mock一个List类型的对象实例,可以采用如下方式:

List list = mock(List.class);   //mock得到一个对象,也可以用@mock注入一个对象

所得到的list对象实例便是List类型的实例,如果不采用mock,List其实只是个接口,我们需要构造或者借助ArrayList才能进行实例化。与Shadow不同,Mock构造的是一个虚拟的对象,用于解耦真实对象所需要的依赖。Mock得到的对象仅仅是具备测试对象的类型,并不是真实的对象,也就是并没有执行过真实对象的逻辑。 Mock也具备一些补充JUnit的验证函数,比如设置函数的执行结果,示例如下:

When(sample.dosomething()).thenReturn(someAction);//when(一个函数执行).thenReturn(一个可替代真实函数的结果的返回值);
//上述代码是设置sample.dosomething()的返回值,当执行了sample.dosomething()这个函数时,就会得到someAction,从而解除了对真实的sample.dosomething()函数的依赖

上述代码为被测函数定义一个可替代真实函数的结果的返回值。当使用这个函数后,这个可验证的结果便会产生影响,从而代替函数的真实结果,这样便解除了对真实函数的依赖。 同时Mock框架也可以验证函数的执行次数,代码如下:

List list = mock(List.class);   //Mock得到一个对象
list.add(1);                    //执行一个函数
verify(list).add(1);            //验证这个函数的执行
verify(list,time(3)).add(1);    //验证这个函数的执行次数

在一些需要解除网络依赖的场景中,多使用Mock。比如对retrofit框架的网络依赖解除如下:

//代码参考了参考文献[3]
public class MockClient implements Client {@Overridepublic Response execute(Request request) throws IOException {Uri uri = Uri.parse(request.getUrl());String responseString = "";if(uri.getPath().equals("/path/of/interest")) {responseString = "返回的json1";//这里是设置返回值} else {responseString = "返回的json2";}return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST, new TypedByteArray("application/json", responseString.getBytes()));}
}
//MockClient使用方式如下:
RestAdapter.Builder builder = new RestAdapter.Builder();
builder.setClient(new MockClient());

这种方式下retrofit的response可以由单元测试编写者设置,而不来源于网络,从而解除了对网络环境的依赖。

单元测试的范围

在Android项目中,单元测试的对象是组件状态、控件行为、界面元素和自定义函数。本文并不推荐对每个函数进行一对一的测试,像onStart()、onDestroy()这些周期函数并不需要全部覆盖到。商业项目多采用Scrum模式,要求快速迭代,有时候未必有较多的时间写单元测试,不再要求逐个函数写单元测试。
本文单元测试的case多来源于一个简短的业务逻辑,单元测试case需要对这段业务逻辑进行验证。在验证的过程中,开发人员可以深度了解业务流程,同时新人来了看一下项目单元测试就知道哪个逻辑跑了多少函数,需要注意哪些边界——是的,单元测试需要像文档一样具备业务指导能力。
在大型项目中,遇到需要改动基类中代码的需求时,往往不能准确快速地知道改动后的影响范围,紧急时多采用创建子类覆盖父类函数的办法,但这不是长久之计,在足够覆盖率的单元测试支持下,跑一下单元测试就知道某个函数改动后的影响,可以放心地修改基类。
美团的Android单元测试编写流程如图4所示。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/607bd9fe.png)
图4 美团Android单元测试编写流程

单元测试最终需要输出文档式的单元测试代码,为线上代码提供良好的代码稳定性保证。

单元测试的流程

实际项目中,单元测试对象与页面是一对一的,并不建议跨页面,这样的单元测试藕合度太大,维护困难。单元测试需要找到页面的入口,分析项目页面中的元素、业务逻辑,这里的逻辑不仅仅包括界面元素的展示以及控件组件的行为,还包括代码的处理逻辑。然后可以创建单元测试case列表(列表用于纪录项目中单元测试的范围,便于单元测试的管理以及新人了解业务流程),列表中记录单元测试对象的页面,对象中的case逻辑以及名称等。工程师可以根据这个列表开始写单元测试代码。 单元测试是工程师代码级别的质量保证工程,上述流程并不能完全覆盖重要的业务逻辑以及边界条件,因此,需要写完后,看覆盖率,找出单元测试中没有覆盖到的函数分支条件等,然后继续补充单元测试case列表,并在单元测试工程代码中补上case。 直到规划的页面中所有逻辑的重要分支、边界条件都被覆盖,该项目的单元测试结束。单元测试流程如图5所示。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/b0f09068.jpg)
图5 单元测试执行流程

上述分析页面入口所得到结果便是@Before标记的函数中的代码,之后的循环便是所有的case(@Test标记的函数)。

为了系统的介绍单元测试的实施过程,本文创建了一个小型的demo项目作为测试对象。demo的功能是供用户发布所见的新闻到服务端,并浏览所有已经发表的新闻,是个典型的自媒体应用。该demo的开发和测试涉及到TextView、EditView、ListView、Button以及自定义View,包含了网络请求、多线程、异步任务以及界面跳转等。能够为多数商业项目提供参照样例。项目页面如图6所示。

![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2015/6a8ce640.jpg)
图6 单元测试case设计

首先需要分析App的每个页面,针对页面提取出简短的业务逻辑,提取出的业务逻辑如图6绿色圈图所示。根据这些逻辑来设计单元测试的case(带有@Test注解的那个函数),这里的业务逻辑不仅指需求中的业务,还包括其他需要维护的代码逻辑。业务流程不允许跨页面,以免增加单元测试case的维护成本。针对demo中界面的单元测试case设计如下:

表1 单元测试case列表
目标页面业务覆盖界面元素逻辑描述最小断言数case名称
创建新闻页面
NewsCreatedActivity
编写新闻1.标题框
2.内容框
3.发布按钮
1.向标题框输入内容
2.向内容框输入内容
3.当标题和内容都存在的时候,上传按钮可点击
3testWriteNews()
输入新闻的金额1.Checkbox
2.金额控件
1.选中免费发布时,金额输入框消失
2.不选免费时可以输入金额
3.金额输入框只接受小数点后最多两位
3testValue()
菜单跳转至新闻列表1.菜单按钮1.点击菜单跳转到新闻列表页面1testMenuForTrunNewsList()
发布新闻1.发布按钮
2.Toast
1.当标题或者内容为空时,发布按钮不可点击
2.编写了新闻的前提下,点击发布按钮
3.新闻发布成功,弹出Toast提示 “新闻已提交”
4.没有标题或者内容时,新闻发布失败,弹出Toast提示“新闻提交失败”
5testNewsPush()、
testPushNewsFailed()
新闻列表页面
NewsListActivity
浏览新闻列表1.列表1.进入此页面后会出现新闻列表
2.有网络情况下,能发起网络请求
3.网络请求需要用Mock解除偶和,单独验证页面对数据的响应,后端返回一项时,列表只有一条数据
6testNewsListNoNetwork()、
testGetnewsWhenNetwork()、
testSetNews()
菜单跳转至创建新闻页面1.菜单按钮1.点击菜单跳转到创建新闻页面1testMenuForTrunCreatNews()
查看详细新闻1.有内容的列表
2.Dialog
1.有新闻的前提下,列表可点击,点击弹出Dialog1testNewsDialog()

接下来需要在单元测试工程中实现上述case,最小断言数是业务逻辑上的判断,并不是代码的边界条件,真实的case需要考虑代码的边界条件,比如数组为空等条件,因此,最终的断言数量会大于等于最小断言数。在需求业务上,最小断言数也是该需求的业务条件。
写完case后需要跑一遍单元测试并检查覆盖率报告,当覆盖率报告中缺少有些单元测试case列表中没有但是实际逻辑中会有的逻辑时,需要更新单元测试case列表,添加遗漏的逻辑,并将对应的代码补上。直到所有需要维护的逻辑都被覆盖,该项目中的单元测试才算完成。单元测试并不是QA的黑盒测试,需要保证对代码逻辑的覆盖。
对表1分析,第一个页面的“发布新闻”的case可以直接调用“编写新闻”的case,以满足条件“2.编写了新闻的前提下,点击发布按钮”,在JUnit框架下,case(带@Test注解的那个函数)也是个函数,直接调用这个函数就不是case,和case是无关的,两者并不会相互影响,可以直接调用以减少重复代码。第二个页面不同于第一个,一进入就需要网络请求,后续业务都需要依赖这个网络请求,单元测试不应该对某一个条件过度耦合,因此,需要用mock解除耦合,直接mock出网络请求得到的数据,单独验证页面对数据的响应。

单元测试并不是一个能直接产生回报的工程,它的运行以及覆盖率也不能直接提升代码质量,但其带来的代码控制力能够大幅度降低大规模协同开发的风险。现在的商业App开发都是大型团队协作开发,不断会有新人加入,无论新人是刚入行的应届生还是工作多年,在代码存在一定业务耦合度的时候,修改代码就有一定风险,可能会影响之前比较隐蔽的业务逻辑,或者是丢失曾经的补丁,如果有高覆盖率的单元测试工程,就能很快定位到新增代码对现有项目的影响,与QA验收不同,这种影响是代码级的。
在本文所设计的单元测试流程中,单元测试的case和具体页面的具体业务流程以及该业务的代码逻辑紧密联系,单元测试如同技术文档一般,能够体现出一个业务逻辑运行了多少函数,需要注意什么样的条件。这是一种新人了解业务流程、对业务进行代码级别融入的好办法,看一下以前的单元测试case,就能知道与该case对应的那个页面上的那个业务逻辑会执行多少函数,以及这些函数可能出现的结果。

[1] http://robolectric.org [2] https://github.com/square/okhttp/tree/master/mockwebserver [3] http://stackoverflow.com/questions/17544751/square-retrofit-server-mock-for-testing [4] https://en.wikipedia.org/wiki/Unit_testing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/478024.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

这三个NLP项目写进简历,网申通过率提高50%

01三大企业级项目项目一京东智能对话系统项目项目简介:智能客服机器人已经成为了客服系统的重要组成部分,帮助人工客服提升工作效率,为企业降低人工成本。作为智能客服的行业先驱,京东多年来致力打造全链路的客服机器人&#xff0…

论文浅尝 | MulDE:面向低维知识图嵌入的多教师知识蒸馏

笔记整理:朱渝珊,浙江大学在读博士,研究方向为快速知识图谱的表示学习,多模态知识图谱。Motivation为了更高的精度,现有的KGE方法都会采用较高的embedding维度,但是高维KGE需要巨大的训练成本和存储空间。现…

LeetCode 453. 最小移动次数使数组元素相等(数学)

1. 题目 给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1。 示例: 输入: [1,2,3]输出: 3 解释: 只需要3次移动(注意每次移动会增加两个元素的值):[1,2,3] > [2…

jupyter文件转换成python文件

method 1 1,在当前文件夹的目录中输入cmd并回车(打开的cmd中会显示出你当前的目录) 2,输入命令: jupyter nbconvert --to script *.ipynb 3,运行完后,当前文件夹目录中的所有.ipynb文件都会转成…

性能优化模式

摘要 性能优化涉及面很广。一般而言,性能优化指降低响应时间和提高系统吞吐量两个方面,但在流量高峰时候,性能问题往往会表现为服务可用性下降,所以性能优化也可以包括提高服务可用性。在某些情况下,降低响应时间、提高…

征稿 | 国际KG大会 IJCKG 2021专辑征文

国际知识图谱联合会议(International Joint Conference on Knowledge Graphs (IJCKG 2021, 与ACM/SIGAI联合办会)今年将于12月6日-8日在线召开。Data Intelligence今年被EI数据库收录后,有幸成为大会推荐期刊,大会优秀论文将推荐在…

200字带你看完一本书,GPT-3已经会给长篇小说写摘要了

文 | 博雯源 | 量子位现在,AI能帮你200字看完一段12万词的长篇小说了!比如这样一段121567词的《傲慢与偏见》原文:▲图源OpenAI官网AI分四个阶段来总结:先把原文总结成276个摘要(24796词),然后进…

LeetCode 1252. 奇数值单元格的数目

1. 题目 给你一个 n 行 m 列的矩阵,最开始的时候,每个单元格中的值都是 0。 另有一个索引数组 indices,indices[i] [ri, ci] 中的 ri 和 ci 分别表示指定的行和列(从 0 开始编号)。 你需要将每对 [ri, ci] 指定的行…

Google Research新成果,让表格理解和检索更上一层楼!

文 | 舞风小兔编 | 智商掉了一地如何更好地理解自然语言查询问题与表格信息?Google Research给出了一个改进版Transformer,一起来看看吧!表格以结构化方式存储信息,广泛地存在于web世界中。表格最为常见的一种用法就是人们查询其中…

论文浅尝 | 用于视觉推理的显式知识集成

论文笔记整理:刘克欣,天津大学硕士链接:https://openaccess.thecvf.com/content/CVPR2021/papers/Zhang_Explicit_Knowledge_Incorporation_for_Visual_Reasoning_CVPR_2021_paper.pdf动机现有的可解释的和显式的视觉推理方法仅执行基于视觉证…

美团O2O排序解决方案——线下篇

背景 针对美团90%的交易发生在移动端的业务特点,我们实现了一套适用于O2O业务的搜索排序技术方案,已在许多产品和子行业中得到应用。在之前的线上篇中,我们已经介绍了服务的框架、排序算法等。本文为线下篇,主要讲述数据清洗、特征…

Github上的一些优秀的知识图谱项目*

Github上的一些优秀的知识图谱项目 Github上开源了很多有关知识图谱的项目,经过调研,整理了一些优秀的知识图谱项目。 利用网络上公开的数据构建一个小型的证券知识图谱/知识库 https://github.com/lemonhu/stock-knowledge-graph 医疗保险领域知识图…

LeetCode 401. 二进制手表

1. 题目 二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)。 每个 LED 代表一个 0 或 1,最低位在右侧。 例如,上面的二进制手表读取 “3:25”。 给定一个非负整数 …

EMNLP'21 | 让压缩语言模型自动搜索最优结构!

文 | Cheney编 | 智商掉了一地既让模型跑得快又好,又要空间占用少,真的有这样效果爆表的操作存在吗?在当前疫情反复的情况下,大家平时出门用健康宝刷脸的频率变得越来越高。如果每次人脸识别都需要等很久的话,那也太让…

直播分享丨前沿技术讲习班:知识图谱前沿技术与应用(CIPS ATT27)

本文转载自公众号:智源社区助手。作为大数据时代重要的知识表示方式,知识图谱是人工智能领域构建和应用知识的新阶段,它能够更好地实现大规模数据的认知与推理。同时,知识图谱和深度学习相互协作,是实现具有强鲁棒性、…

美团O2O排序解决方案——线上篇

美团的愿景是连接消费者和商家,而搜索在其中起着非常重要的作用。随着业务的发展,美团的商家和团购数正在飞速增长。这一背景下,搜索排序的重要性显得更加突出:排序的优化能帮助用户更便捷地找到满足其需求的商家和团购&#xff0…

手把手教你模型选择,模型评估

数据来源是:头条新闻数据,经过处理之后的部分数据如下: 首先通过交叉验证,取选择模型: from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.s…

LeetCode 1030. 距离顺序排列矩阵单元格(排序Lambda表达式BFS)

文章目录1. 题目2. 解题2.1 multimap2.2 Lambda 表达式排序2.3 BFS搜索1. 题目 给出 R 行 C 列的矩阵&#xff0c;其中的单元格的整数坐标为 (r, c)&#xff0c;满足 0 < r < R 且 0 < c < C。 另外&#xff0c;我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格…

干货 | 带你理解对比学习损失函数的性质以及温度系数的作用

文 | Feng源 | 对白的算法屋很多小伙伴都了解对比学习&#xff0c;但要说温度系数的作用可能就不太清楚了。卷友们好&#xff0c;我是对白。对比学习中的温度系数是一个神秘的参数&#xff0c;大部分论文都默认采用小的温度系数来进行自监督对比学习&#xff08;例如0.07&#…

论文浅尝 | Data Intelligence第4期正式上线啦

本文转载自公众号&#xff1a;数据智能英文刊各位关注DI期刊的专家学者和业界同仁&#xff1a;Data Intelligence第4期正式上线啦&#xff01;本期共出版2篇研究性论文&#xff0c;5篇数据论文&#xff0c;1篇实践类论文。点击每篇文章标题下方的二维码可直达论文全文页面。敬请…