科研论文复现

目录

0 引言

1 在你阅读科学论文之前

1.1 先找找开源资源,避免重复编程

1.2 寻找实现你目标的更简便的途径

1.3 注意软件专利

1.4 学习相关领域的更多论文

1.5 保持学习动力

2 三种论文分类

2.1 开创性论文

2.2 抄袭论文

2.3 垃圾论文

3 如何阅读科学论文

3.1 找到正确的论文

3.2 不要在屏幕上阅读

3.3 良好的时间和地点

3.4 标记和注释

3.5 了解所有术语的定义

3.6 在结论中寻找统计分析

3.7 确保论文结果满足你的需求

3.10 理解变量和操作符

3.11 了解数据流程

4 原型

4.1 原型解决方案

4.2 原型有助于调试过程

4.3 从原型中获得改进

4.4 验证论文给出的结果

5 选择正确的语言和库

5.1 现有系统

5.2 预测复现代码的未来用途

5.3 完全或部分解决算法的可用库

6 算法复现

6.1 正确得选择精度

6.2 归档编程过程

6.3 在代码中添加对论文的引用

6.4 避免使用数学符 命名变量

6.5 第一遍不做优化

6.6 计划创建一个API/p>

7 调试

7.1 将结果与其他复现进行比较

7.2 与读过该论文的人交谈

7.3 将变量形象化

7.4 用于测试的数据集

8 结论


0 引言

人类理性活动有三种方式:

(1)纯逻辑的推理和思辨方式。

(2)归纳法

(3)演绎法

中国是技艺型文化,西方是哲科型文化,对思维逻辑性很高。所以纯逻辑的推理和思辨方式对于科研很重要!科研论文代码复现的能力也很重要!

 

1 在你阅读科学论文之前

在你阅读科学论文并准备复现之前,你应该注意以下几点,并保证你能够根据这些点一一进行自我检查。

1.1 先找找开源资源,避免重复编程

除非是你想要通过算法复现更深入地学习一个领域的知识,你才有必要亲自去复现它们。如果你并不是想要复现整个论文的算法,而只是想应用它们,那你就应当在做任何复现工作之前,花上几天时间在 络上寻找开源资料。试想一下,你是愿意花两天时间找开源代码,还是希望浪费两个月时间复现一个已经开源的代码呢/p>

1.2 寻找实现你目标的更简便的途径

问问你自己你的目标是什么了达到你的目标,是不是有更简便的解决办法呢能不能用其他的技术使只能得到你想要的结果的80%,这也意味着你不需要再去亲自动手复现整个代码,这样的话,你也许会在接下来的两天内将现有的算法或者开源的算法运行起来。关于这方面如果你想要了解更多,可以查看我的文章《20/80生产力法则》。

1.3 注意软件专利

如果你在美国,你应该注意软件专利。有些论文申请了专利,如果你把它们的算法应用到商业用途,你可能会陷入侵权问题。

1.4 学习相关领域的更多论文

假如你在阅读一篇关于在神经科学背景下使用支持向量机(SVM)的论文,那么你应该读一段机器学习的简单介绍以及不同可以代替支持向量机的分类器,并且你应该阅读计算神经科学的概论性的文章来了解该领域目前正在进行什么样的研究。

1.5 保持学习动力

如果你从来没有复现过一篇论文的算法,或者你对该论文的领域非常陌生,那么论文的阅读就会非常困难。无论发生什么,不要让大量的复杂的数学方程使你气馁。此外,欲速则不达,尽管你的理解速度比你想象的要慢,只要你继续努力,你就会慢慢地、稳步地理解论文所提出的概念,并一个接一个地克服所有困难。

2 三种论文分类

随机选择一篇论文,然后立刻开始代码复现并不是一个好主意。从知识库里可以找到大量的论文,这也意味着其中不乏有许多垃圾论文,就我而言,我认为论文可以分为三类:

2.1 开创性论文

2.2 抄袭论文

一些研究团队一直关注开创性团队的工作,并对其成果提出改进意见然后发表他们的改进结果。这些论文中,许多论文缺乏适当的统计分析,并常常会得到错误的结论,认为这些改进确实优于原来的算法。大多数时候,除了带来了额外的复杂工作以外,他们实际上没有提出任何创新或更好的东西。但并不是所有的模仿者都是不好的,有些也能给出新的见解,但是很少。

2.3 垃圾论文

有些研究员并不知道他们在做什么,或者他们本身目的就是邪恶的。他们只是试图在自己所属的学术机构或者他们任教的领域保持地位和特权。为此他们需要通过发表一些论文来获得必要的资金资助,他们中有些诚实的人会告诉你实验失败了,并且在只有N%的时间内是准确的(N是一个糟糕的值)。但是也不乏其中邪恶的人会撒谎说取得了巨大的成功。当你读了一段时间的文献资料后,你就很容易区分出垃圾论文,并扔掉他们了。

3 如何阅读科学论文

关于这个主题已经有很多文章了,所以我只是简单提一提。一个好的学习起点是: Srinivasan Keshav的论文如何阅读一篇论文。下面是我在阅读文献时发现的一些有用的观点:

3.1 找到正确的论文

3.2 不要在屏幕上阅读

打印文献资料并阅读纸质版本。另外,不要为了在每页上打印更多的内容而降低字体大小。虽然这样做你可以节省三张纸,但是你会浪费时间,因为你会因为更快地阅读这些小字体而感到疲倦。适合阅读的字体大小在11到13字 之间。

3.3 良好的时间和地点

不要在半夜看论文,要在白天你的大脑还清醒的时候看。此外,找一个安静的地方,使用良好的照明。当我阅读时,我会在桌面上放置台灯,并且光线对准我的文件。

3.4 标记和注释

用记 笔标出重要信息,并在空白处写下你阅读时脑海中闪现的任何想法。

3.5 了解所有术语的定义

当你习惯主要阅读新的文章和小说时,你的大脑会训练你用上下文作为推理工具,为你不认识的单词填入意义。阅读科文献却是一种不同的方法,最大的错误之一就是推测出一个单词的错误意思。例如这句话:“The results of this segmentation approach still suffer from blurring artifacts”。在这里,“segmentation”和“artifacts”这两个词在英语中具有一般意义,但在计算机视觉领域中又具有特殊意义。如果你不知道这些词在这篇文章中有一个特定的意思,那么当你不刻意注意这些术语时,你的大脑就会填入一般的解释,为此,你可能会错过一些非常重要的信息。因此你必须:

  • 避免推测单词的意思,每当有疑问查找这些术语的专业解释,通常是通过该领域的主要论文文献;

3.6 在结论中寻找统计分析

3.7 确保论文结果满足你的需求

3.10 理解变量和操作符

论文复现过程中的主要任务是将文章中的数学方程转化为代码和数据,这意味着在开始编写代码之前,你必须100%理解这些方程和推导这些方程的过程。例如,“C = A . B可能有不同的意思,A和B可以是简单的数字,还有”.“操作可能只是一个数量积,在这种情况下,C是两个数字A和B的数乘积。但是也许A和B是矩阵,而“.”操作表示矩阵点乘,在这种情况下,C就是矩阵A和B点乘结果。另一种可能性是,A和B是矩阵,“.”是逐项积算子,在这种情况下,每个元素C(i,j)都是A(i,j)和B(i,j)的乘积。变量和运算符的符 可以从一种数学规定更变为另一种数学规定,也可以从一个研究小组规定更改为另一个研究小组的规定。确保你知道每个变量是什么(标量、向量、矩阵或其他东西),以及每个操作符对这些变量所做的操作。

3.11 了解数据流程

一篇论文由一连串的方程式构成。在开始编码之前,你必须知道如何将方程N的输出插入方程N+1的输入。

4 原型

一旦你阅读并理解了这篇论文,接下来就该创建一个原型了,这是非常重要的一步,避免了浪费时间和资源。用C、C++或Java等语言实现复杂算法可能非常耗时,即使你对这篇论文复现有一定的信心并且认为这个算法会成功,它仍然有可能完全失败。所以你想要以偷懒的方式,尽可能快地编码,只是为了检查它是否正常工作。

4.1 原型解决方案

最好的解决方案是使用更高级语言或环境,如Matlab、R、Octave或SciPy/NumPy。用C++表示一个数学方程,然后打印结果来手动检查并不容易。相反,在Matlab中编写方程,然后打印出来却是非常简单的。在C++中需要两到三周的东西在Matlab中可能只需要两天。

4.2 原型有助于调试过程

拥有原型的一个优点是,将来编写C++版本时,你将能够比较Matlab原型和C++实现的结果。这将在下面的“调试”小节中进一步开发。

4.3 从原型中获得改进

你肯定会在原型中犯程序设计错误,这是一件好事,因为你将能够确定流程或数据处理的困难所在。当你编写C++版本的代码时,你将知道如何更好地构建代码,并且你将生成比没有原型化步骤时更简洁、更稳定的代码(这是Frederick Brooks在《The Mythical Man-Month》中提出的“抛弃系统”思想)。

4.4 验证论文给出的结果

5 选择正确的语言和库

在这个阶段,你必须清楚地理解文献中提出的算法和概念,并且必须有一个运行的原型,以确保算法确实在你输入想要的数据是能够得到正确的结果。现在是进入下一个步骤的时候了,该步骤包括使用你希望在复现中使用的语言和框架。

5.1 现有系统

很多时候,编程语言和库是由现有系统决定的。例如,在用Java编码的库中,你有一组用于图片光照规范化的算法,并且你想要从该文献中添加一个新的算法。在这种情况下,很明显,你不会用C++编写这个新算法,而是用Java编写。

5.2 预测复现代码的未来用途

如果没有现存系统强制你使用一种语言,那么应该根据算法的未来用途来选择该语言。举个例子,如果你相信在四到六个月,可能你的应用程序可以移植到iPhone,那么你应该选择C / C++ / Java,因为它是不需要一切从头开始将代码整合到一个Objective – C应用程序的简便方法。

5.3 完全或部分解决算法的可用库

可用库的差异还可以决定再现代码的编程语言。假设你希望实现的算法使用了注明的代数技术比如如主成分分析(PCA)和奇异值分解(SVD),虽然你也可以重写他们的代码,并花上一周时间调试,但是我建议你用一个已经实现了这些技术的库,并使用这个库的约定和矩阵类编写实现代码。理想情况下,你应该能够将复现代码分解为子任务,并尝试尽可能找到已经实现了这些子任务的库。如果你找到一组只适用于给定语言的合适的库,那么你应该选择该语言。另外,请注意库的选择应该在引用现有代码和最小化依赖关系之间进行权衡,尽管为实现所需的每个子任务的编程代码很好,但是如果这需要创建超过20个不同库的依赖关系,那么它可能不太实用,甚至可能危及实现的复现算法的稳定性。

6 算法复现

以下是我在复现论文算法的一些经验。

6.1 正确得选择精度

应该仔细选择你将用于计算的类型。通常使用double而不是float会更好,虽然内存的使用会增大,但计算的精度也会提高。此外,你应该了解32位和64位系统之间的差异,只要可以,就创建自己的类型来封装底层类型(float或double、32位或64位),并在代码中使用这种类型。这可以通过定义为C/ C++或Java中的类来实现。

6.2 归档编程过程

尽管过度文档化确会显著地降低项目的速度,但是对于复杂科学论文的实现,你需要对所有内容进行存档和解释,即使你是这个项目的唯一工作人员,你也应该记录你的文件、类和方法。选择一个像Doxygen或reStructuredText这样的管理,并坚持下去。在开发的后期,你可能会忘记某个类是如何工作的,或者你是如何实现某个算法,你会感谢自己为代码编写了文档!

6.3 在代码中添加对论文的引用

6.4 避免使用数学符 命名变量

6.5 第一遍不做优化

把所有的优化留到以后。因为你永远无法绝对确定代码的哪一部分需要优化。每当你看到一个可能的优化,添加一条注释,并用几行代码解释如何实现优化,例如:

这样,以后就可以找到代码中所有可能进行优化的位置,并获得关于如何优化技巧。一旦复现完成,你就可以通过运行诸如Valgrind之类的分析器或你使用的编程语言中的任何可用工具来找到优化的位置。

6.6 计划创建一个API/h2>

如果你计划使用当前代码作为API的基础并且此API随着时间的推移而将会增长,那么你应该了解创建实际可用的接口的技术。为此,我推荐Joshua Bloch在他的作品《如何设计一个好的API》(How to Design a Good API)中总结的“针对库编码”技术。

7 调试

实现一个新的算法就像烹饪一道你从来没有吃过菜。即使它尝起来不错,你也永远不知道这是不是本来应该有的味道。现在我们很幸运,因为编程与烹饪有不同之处,程序开发有一些有用的技巧来增加我们对复现的信心。

7.1 将结果与其他复现进行比较

消除错误的一个好方法是将代码的结果与相同算法的现有实现的结果进行比较。假设你正确地完成了上面一节中的所有任务,但是你没有找到任何可用的算法复现方法,因此,你在此阶段拥有的惟一其他复现方法就是编写的原型。
因此,核心想法是比较原型和复现的每一步结果,如果结果不同,那么这两种中必有一个错误了,你必须找出哪个错了和错误的原因。注意精度可以改变(原型可以给出x = 1.8966,而复现代码x = 1.8965),比较时当然应该考虑到这一点。

7.2 与读过该论文的人交谈

7.3 将变量形象化

在开发过程中,关注算法使用的变量的内容总是好的。我说的不仅仅是打印矩阵和数据中的所有值,而是找到适合于实现中的任何变量的可视化技巧。例如,假设一个矩阵表示图像的梯度,那么在编程和调试期间,应该弹出一个窗口,显示梯度图像,而不仅仅是图像矩阵中的数值。这样,你可以将把实际的图像与正在处理的数据关联起来,并且你将能够检测到其中一个变量何时出现问题,而这又能显示可能的错误。创造性的可视化技巧包括图像、散点图、图表或任何不只是一个愚蠢的1000个数字的列表的东西,你可以根据它联想到脑海中的图像。

7.4 用于测试的数据集

生成用于实验复现的数据可能非常耗时。只要有可能,就尝试寻找数据库(脸部数据库、文本提取数据库等)或生成此类数据的工具。如果没有,那么不要浪费时间手工生成1000个样本,用20行代码编写一个快速数据生成器创造他们。

8 结论

 

 

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年1月6日
下一篇 2022年1月6日

相关推荐