21/8/9 读书笔记 命名规范 模型评估和选择

21/8/9 读书笔记

Code Complete 变量命名

注意本章讨论的是一个好的变量命名规范应该长什么样子,而不是探讨规范本身。选择一种好的代码规范对于编程来说很重要,而对于不同人来说,适合的命名规范也是不同的。希望能从中一些关于变量命名规范思考的思路。

变量命名的非正式规范

  • 变量名的长度在1015左右正常,820左右能够接受
  • 变量的作用域越大,其命名应更加复杂
  • 为不同场景下的同名变量添加限定词前缀,以区分其功能
  • 称Total、Sum、Average等数值相关的描述词为计算值限定词,应保持计算值限定词作为变量后缀
  • Num这一计算值限定词即可表示数量也可表示某对象对应的序号,应尽量避免使用,该用Total或Index
  • 循环下标变量避免使用i、j、k,除非只是简单循环
  • 状态变量不应具有flag之类的含糊的词,而应该完全描述一个状态
  • 状态变量赋值应该利用枚举类型或者具名变量(比如宏),避免采用魔数
  • 临时变量不应使用temp。如果你尚且不知道该变量的作用,就根据其赋值的格式进行命名。比如对于数值计算的中间变量可以称为expression,对于临时数组可以称为list
  • 布尔变量命名典型限定词有donesuccessfounderroravailable
  • 布尔变量命名可以使用is前缀,但是这样可读性较差,毕竟isFound=true看起来不比found=true简洁
  • 布尔变量命名避免采用否定的变量名,比如notFound。
  • 枚举变量命名应为每个变量名附带前缀来辨识其属于同一个组。在使用<组名>.<变量名>访问枚举变量的语言中可忽略这一条。
  • 枚举类型名总以复数形式出现
  • 全局变量采用g_前缀
  • 具名变量全大写
  • 类成员变量用m_,表示该变量只在该类中使用
  • 通常来说,类型名首字母大写,局部变量首字母小写。在大小写不敏感的语言中采用t_前缀来标识类型名。
  • 遇到相似的变量名时,尽量将区别集中在结尾或开头
  • 尽可能不要使用数字,同时不要过分相信大小写

标准前缀

基于匈牙利命名法提出了一套具有通用含有的标准前缀,分为用户自定义类型缩写(User-Defined Type Abbreviations, UDT)和语义前缀(Semantic Prefixes):

  • UDT:描述类型,在不同项目中有用户自定义。下面仅列出一些经常使用的约定俗成的缩写词。
    • ch:字符 CHaracter
    • scr:屏幕范围 SCreen Region
    • doc:文档 DOCument
    • pa:段落 PAragraph
    • sel:选取项 SELection
    • wn:窗体 WiNdow
  • 语义前缀:具有通用性,作为变量前者来指示变量语义。
    • c:数量,对应count
    • first:数组中第一个需要处理的元素
    • g:全局变量,对应global
    • i:数组下标,对应index
    • last:数组中最后一个需要处理的元素
    • lim:数组中需要处理的元素上限
    • m:类一级的变量
    • max:数组中绝对的最后一个元素
    • min:数组中绝对的第一个元素
    • p:指针

如何缩写

不要试图应用所有规则,因为他们自相矛盾;注意一旦规则确定,那么最好对所有变量均是如此。

  • 使用标准的缩写
  • 取出所有非前置元音,比如computer编程cmptr,employee变成emplye。
  • 使用每个单词的第一个或前几个字母,或者保持第一个和最后一个字母
  • 保留每个音节中最突出的发音
  • 去除后缀ing、ed,虚词or、and、the等

注意创建一个能够被读出来的缩写,并且借助字典替换词,来防止变量的缩写撞车。注意对于一个单词的缩写应该在全局上一致。

在工程中可以在代码中添加缩写对照表,来指导缩写词含义。


机器学习 模型评估和选择

过拟合和欠拟合的本质:认识到训练样本既有一般性,也特殊性。学习算法对训练样本的一般性质尚未学好,导致欠拟合;对训练样本的特点当做了所有潜在样本的一般性质,导致过拟合。过拟合不可避免,这是建立在\(P\neq NP\)​的基础上的,如果避免过拟合,就相当于我们用经验误差最小化就能获取最优解。

划分方法评估集和测试集的方法:

  • 留出法:直接分割。通常需要多次分割取平均值作为最终测试评估结果。
  • N次K折交叉验证:一次交叉验证将样本集分割为K份,取K-1份测试,共做K次。N次之间的区别在于进行了不同的K-划分。
  • 自助法:可放回地取出样本作为训练集。从统计意义上有越36.8%的样本不会被取出,作为测试集。会改变样本的分布,尽量不使用,适用于数据集较小导致难以划分时

对比不同算法的泛化性能时,进行上述划分后,将训练数据还要再分为训练集和验证集,验证集用于模型选择和调参。

性能度量

二分类和多分类问题中的错误率/精度,评估了由多少样例被正确分类;查准率/查全率(实际就是数据挖掘中提到的准确率/召回率)分别对应预测为正例中有多少是真实正例/真实正例里有多少分类为正例。

查准率和查全率是矛盾的,二者不可兼得。对一个模型来说,将样本逐个输入,记录下每次输入后的查准率和查全率,分别做轴形成P-R图。如果新模型的P-R图完全将之前的模型的P-R图完全包裹,则认为优于之前的模型。

不完全包裹时较难判定,故考虑判断P-R图面积,但是面积也较难求。因此退而求其次,引入平衡点(Break-Even Point, BEP)对应“查重率=查全率”时的二者取值,BEP越大性能越好。

引入\(F1\)度量来描述查重率和查准率的调和平均值,认为调和平均值更大性能更好;如果需要对查重率或查全率设置一定偏好,可以引入\(F_\beta\)度量来描述二者的加权调和平均值,为我们提供了更加灵活的方法:

\[\frac{1}{F_1}=\frac{1}{2}\cdot\left(\frac{1}{P}+\frac{1}{R}\right) \frac{1}{F_\beta}=\frac{1}{1+\beta^2}\cdot\left(\frac{1}{P}+\frac{\beta^2}{R}\right) \]

ROC和AUC是另一套基于排序“误差”的评价度量,针对可以对分类问题产出01之间的一个评价概率的模型。其将所有测试点的输出概率排序,然后取一个概率作为“截断点”,认为小于该概率的样本为反例,反之为正例,计算此时的真正例率和假正例率。当截断点在01上连续取时,真正例率和假正例率呈现出ROC曲线,AUC描述其与假正例率轴围成的面积。AUC越高,认为一个模型的性能更优。

代价敏感错误率契合了“非均等代价”,即不同分类错误导致的损失不同。其计算方式是将分类错误的情况分别赋予权重\(cost_{01}\)\(cost_{10}\)后计算总误差。我们和考虑ROC时一样,认为模型产出的是分类概率,让截断点在0~1上连续取。对于每种截断点取值,定义正例概率代价和归一化代价:

\[P(+)cost=\frac{p\times cost_{01}}{p\times cost_{01}+(1-p)\times cost_{10}} \\ cost_{norm}= \frac{FNR\times p\times cost_{01}+FPR\times (1-p)\times cost_{10}}{p\times cost_{01}+(1-p)\times cost_{10}} \]

其中\(p\)​​表示在该截断点情况下样本中样例正例的比例;FPR是假正例率,FNR是假正例率;

归一化代价和正例概率代价形成的曲线称为代价曲线,其和正例概率代价轴围成的面积称为期望总体代价。期望总体代价越小,学习器性能越高。关于代价曲线的详细理解请参阅代价曲线

假设检验

泛化误差和测试误差是两个不同的概念。我们希望通过测试集给出的测试误差能够反映泛化误差,因此需要回答的问题就是“有多大把握认为学习器的泛化误差小于等于测试误差”。而这是需要通过统计假设检验来完成的。

假设,就是指对于学习器的实际泛化误差的分布的一种猜想。更通俗来说,就是猜想“泛化误差不大于某个值”。然后,我们需要利用置信度来判断该假设是否能够被接受。

通常来说,在已知测试误差时,泛化误差的概率密度符合二项分布,并在测试误差值的点取得最大值。此时可以利用二项分布下的概率函数来计算置信度。

然而,很多时候我们将会进行多次测试,因此得出多个(假设有k个)测试误差,这多个测试误差可以计算出平均值\(\mu\)​​和方差\(\sigma^2\)​​,假设泛化误差是\(\epsilon_0\)而使得变量\(\tau_t=\frac{\sqrt{k}(\mu-\epsilon_0)}{\sigma}\)​​​​是服从自由度为k-1的t分布的,由此能够根据t分布的函数来进行置信度的评判。

当我们需要判断两个学习器的相对性能时,还可以采用:

  • 交叉验证t检验:对于k折分割后的数据块的集合,假设学习器A和B在第i个数据块上的测试误差是\(a_i\)\(b_i\),那么当学习器A和B性能相同时,我们期望所有的差值都是0。由此我们得到k个\(\delta_i=a_i-b_i\),并且变量\(\tau_t=\frac{\sqrt{k}(\mu_\delta)}{\sigma_\delta}\)服从t分布,可以判断计算得出的\(\tau_t\)​是否处在置信度范围内来判断学习器A和B的性能是否有显著差异
  • McNemar检验:对于二分类,我们认为学习器A和学习器B性能相同时,A分类正确而B错误的概率\(e_{10}\) 应该等于 A分类错误而B正确的概率\(e_{01}\)。认为\(\tau_{\chi^2}=\frac{(|e_{01}-e_{10}|-1)^2}{e_{01}+e{10}}\)应该符合自由度为1的\(\chi\)​分布。参考链接,这里似乎是采用了Yates连续性修正。
  • Friedman检验:为了解决多种算法在的性能比较问题,采用了基于算法排序的方法。每次测试后对每个算法的测试误差进行排序,赋予不同的序值,如果误差相同则平分序值,进行多次试验。如果我们假设多个算法均一致,那么多次测试后,每个算法得到的平均序值应该相同。如果认为多个算法性能不相同,再使用Nemenyi后续检验,对算法间平均序值差进行评判,认为当序值差处在\(CD=q_\alpha\sqrt{\frac{k(k+1)}{6N}}\)内,则二者无显著差异,其中\(q_\alpha\)是Turkey分布下置信度\(\alpha\)时对应的临界值,k是算法数量,N是测试轮数。参考链接

偏差和方差

模型的期望输出指在不同训练集下,模型的输出的平均值。偏差描述了模型的期望输出与真实标记之间的差,方差描述了在训练集不同时输出结果不同导致的方差。此外还有噪声,与训练集有关。根据对于期望泛化误差的分解,我们可以从数学角度证明泛化误差可分为偏差、方差和噪声。

偏差和方差是有冲突的。学习器训练不足时,偏差较大,拟合能力不强,训练数据很难对最终输出结果产生扰动,使得方差较小;学习器训练充足时,偏差较小,拟合能力很强,导致训练集的微小变动都会造成模型的显著变化,使得方差较大。通常来说,训练程度越高,偏差越小,方差越大,我们期望在偏差和方差中得到平衡,这也为应该避免欠拟合和过拟合提供了理论依据。

posted @ 2021-08-09 16:12  neumy  阅读(277)  评论(0)    收藏  举报