21/8/17 读书笔记 软件协同构建 特征选择和稀疏学习
21/8/17 读书笔记
Code Complete 协同构建
协同构建包括结对编程、正式检查、非正式复查、文档阅读等让开发人员共同承担创建代码和其他产品责任的技术。
测试和协同构建所能发现的缺陷是不同的。由人进行的复查能够发现不恰当的注释、硬编码的变量值、重复的代码模式等,这些是测试所不能覆盖的。这也意味着测试和协同构建都需要使用。
协同构建、复查代码是培养新人提高代码质量的好机会。
协同构建使得团队对代码具有集体所有权,而不会过度依赖于特定的开发者,这提高了代码的质量,也降低了开发人员离职带来的影响。
结对编程要求一个程序员敲代码,另一个程序员注意是否有错误以及一些策略上的问题。双方应该对代码有着一致的标准化规范,以免在这种偶然性质的问题上陷入争论。在简单的问题上不要强迫性使用结对编程。结对编程中进行有规律的轮换有助于知识的传播。
正式检查(详查)是一种特定的形式化的复查,其专注于找出缺陷而不是修复缺陷,能够发现大量的代码中的缺陷。详查的一个关键特征就是形式化,每个人都需要扮演明确的一个角色,而且注重详查会议在发现缺陷中的作用。详查需要参与者经过一定的培训或者具有一定的经验。详查是工作性质的,这意味着不应该引入行政人员,故存在三种主要角色:
- 主持人:分派详查任务、跟踪任务完成情况、总结详查报告等
- 作者:代码的实际编写者,负责解释代码中不清晰的部分,以及解释那些看起来错了的地方实际上为什么可以接受。
- 评论员:设计人员、架构师、测试人员都可以担任,负责找出缺陷。他们与详查代码相关但是不直接参与编写。
- 记录员:负责详查会议记录。
详查一般遵循特定的步骤:
- 计划:作者将设计或代码提交给主持人,主持人需要决定找谁作为评论员,并进行任务的分派以及核查表的发放。
- 概述:作者向评论员就设计或代码的技术背景进行描述和讲解。良好的代码应该能够自我解释,因此概述只是自解释性的一个补充。
- 准备:评论员独立地对设计或代码进行详查,找出错误。
- 评论员在准备阶段可以被赋予一定的特定视角(比如假设作为客户或者测试人员)或者特定详查场景(比如检查是否满足某种需求),来提高评论员之间找出的缺陷的差异性。
- 详查会议:详查会议是所有相关人员进行对缺陷进行讨论的平台,重点是找出错误而不是修复错误。
- 详查报告:对所有会议上发现的缺陷进行总结,并且给定每个缺陷的严重程度。这一份报告还用于总结团队中经常出现的缺陷,来构成一张通用核查表,在每次详查的准备工作时发放给评论员。
- 返工:总结的缺陷需要进行修复,通常由作者完成。缺陷的判定是有争议的,作者应该对缺陷的修复工作具有绝对话语权,他需要独立思考每一个问题,以判断是否这是不是一个真正的缺陷。
- 跟进:主持人需要进行跟进,来确认缺陷是否被修复了。
- 第三小时会议:由于详查会议严格禁止对问题实际解决方案的讨论,因此会后可以另外开一个工作会议专门供探讨如何进行具体的修复解决。
其他介绍的协同构建技术包括:
- 走查:非正式的复查,比详查更低效,以作者为协作的中心,注重为一个问题提供多个不同的视角,以补充作者个人的视野。走查对于人员经验的要求不高,而且精力需求更少,有效地利用每个参与者的时间。不理智的走查通常弊大于利,不要过高估计走查的作用。
- 代码阅读:与详查相比,更多注重对于代码的阅读而不是会议讨论,适用于地理位置分散的开发团队。
机器学习 特征选择和稀疏学习
给定一个属性集,我们将属性称为特征,其中当前学习任务有关的属性称为相关特征,其余称为无关特征。我们常常需要对属性集进行处理以得到其中的相关特征,这一方面是为了应对维度灾难,另一方面也是为了降低学习器学习的难度。
我们需要注意相关/无关特征是根据当前的学习任务来决定的,而不是基于属性本身的性质。冗余特征指属性集中那些可以由其他特征推演出来的特征,比如对长方形来说面积可以由长和宽得出。冗余特征不一定是无关特征,因为冗余特征实际上是一种“中间概念”,有时当我们需要考虑中间概念时,冗余特征反而降低了学习器学习的难度。
由于在所有可能的子集上进行筛选是计算不可行的,我们唯一的方法就是利用贪心的策略去寻找合适的局部最优子集。因此特征选择的两大核心问题在于如何评价一个特征子集的特征选择效果(子集评价),以及如何从一个子集根据评价结果生成下一个特征子集(子集搜索)。子集评价一般采用之前学到的信息增益或其他多样性度量,判断当前的特征子集提供了更多有助于分类的信息。子集搜索分为前向、后向和双向,主要区别在其拓展子集中特征的方向。
子集评价和子集搜索构成了一个特征选择方法,常见的特征选择方法分为:
- 过滤式选择(filter):在学习器训练前就进行特征选择,因此过滤式选择与学习器无关。过滤式选择需要尽可能找到从全局统计角度来说更好的那个特征子集,文中介绍的Relief方法利用了“相关统计量”来描述每一个特征的重要性。
- 包裹式选择(wrapper):特征选择和学习器训练独立进行,但直接用学习器训练后的性能作为特征子集质量的评价指标,即将特征子集的选择方式也作为学习器的一部分进行学习。这种选择方法致力于得到最适合当前学习器的特征子集,训练开销更高。文中介绍了基于拉斯维加斯方法(Las Vegas Method)框架下的随机策略的LVW(Las Vegas Wrapper)算法。
- 嵌入式选择(embedding):将特征选择过程和学习器的训练过程结合到一起,即在学习器训练的过程中就完成了特征选择。典型的嵌入式选择方法就是L1正则化,在线性回归模型下使用L1范式更倾向于形成稀疏解,即求得的系数向量\(\bold w\)有更少的非零分量,使得最终模型中出现的特征尽可能地少,这就达到了特征选择的效果。
拉斯维加斯方法和蒙特卡罗方法都是基于随机策略的方法,二者均能在不限时间的前提下得出满足要求的解。区别在于,时间有限时,前者要么给出解要么给不出解,后者一定给出解但是不一定符合要求。
在特征选择中,我们认为特征具有稀疏性,即矩阵中很多列与当前学习任务无关 ,因此我们将其去除。这种稀疏性被去除后有助于我们降低学习任务的难度和提高学习的可解释性。而另一种稀疏性是不感兴趣的元素(比如零元素)并不整行或整列存在,而是零散地分布在数据中,典型的例子就是文本数据的词频统计数据,每个单词占一列,这一列中大部分都是零元素,但是每一列数据都不可忽略。事实上,这种类型的稀疏表达形式有助于学习任务,比如线性的SVM在文本数据上的优异性能正得益于高度稀疏性使得大多数问题变得线性可分。而且稀疏矩阵目前已经有了很高效的存储方式,不会占用很多存储空间。因此我们认为对于普通非稀疏数据,我们可以将其转变为稀疏性更高的稀疏表示(sparse coding),以提高学习器性能。
对于文本数据,我们可以采用字典来将其转变为词频矩阵。而对于更一般的数据,我们也需要学习一个“字典”,来将其稀疏化,这个过程称为字典学习,又称为稀疏编码(前者偏重字典的学习过程,后者偏重利用字典将样本进行稀疏化处理)。在字典学习中,我们需要指定“词汇量”,即对应稀疏化后数据的维度,规定了字典的规模,从而指定了稀疏程度。
我们定义\(k\)是词汇量,每个样本\(\bold x_i\)有\(d\)个属性,其中\(\bold D\in R^{d\times k}\)表示“字典”矩阵,\(\bold \alpha_i\)表示\(\bold x_i\)的稀疏表示。上式中第一项尽可能缩小利用字典处理后稀疏表示和原始样本间的差异以使得稀疏表示能够更好重构原始数据,第二项使得稀疏表示尽可能稀疏化(利用L1正则化)。
压缩感知(compressed sensing)关注如何利用信号在某个变换域内的稀疏性来从观测样本中恢复原信号。我觉得某乎上的这篇文章对理解压缩感知很有帮助,对稀疏性和不相关性进行形象的介绍。相比之下,书中举了一个例子,就是客户对不同书籍的喜好程度评分,如果我们将其视作一个“信号”并只考虑题材对客户评分的影响,那么由于题材数远小于书籍数,因此“题材域”上该信号是稀疏的,因此我们能通过少数的几个客户的评分情况来感知整个客户群体对不同书籍的喜好程度。这个例子个人感觉不如上文提到的某乎文章中举的信号处理的例子更加容易理解。
压缩感知更应该称为compressed sampling。奈奎斯特采样定理认为采样频率应该比信号最高频率的两倍才能保留模拟信号的全部信息,其考虑的是等间距采样,是保留全部信息的一个充分非必要条件。压缩感知的本质是通过更少的(非等间距)采样来重构数据,从形式上看就是在采样的过程中就将数据(相对于奈奎斯特采样定理而言)压缩了。

浙公网安备 33010602011771号