最近看了几期CSDN上面的机器学习培训,说实话,对于培训讲师的英文水平和代码规范实在不敢恭维。为什么代码规范能和英文水平联系起来呢?别忘了,我们一般都是用英文写代码的。一个人搞不清楚单复数和词形,写出来的代码看着就很难受。
如果我一直都看国内讲师的节目,也许挑不出毛病。恰好我是先看的斯坦福,coursera等课程,也看了python for data analysis等规范的教材。所以一开始就养成了良好的编程习惯。
子曰:名不正则言不顺。
作为python的机器学习工程师,首先要和其他python程序员一样遵守python代码规范。
以下是Python官方的代码规范:
https://www.python.org/dev/peps/pep-0008/
有些内容,我看了以后还是挺吃惊的。比如python对于数学公式的规范:
#正确的示范
y = a*x + b
#错误的示范
y = a * x + b
想知道为什么?自己去看。
代码规范的核心就是要让代码可读,如果代码规范会使得代码的可读性下降,那么可以不遵守代码规范。
代码规范中提到内容,这里不再重复。以下是我对代码规范的补充。
很多人写文章谈代码规范,都是翻译或者看了外国人写的文章,对于中国人经常犯的错误,闭口不提。但是我发现,程序员写代码用的是英语,但是却不注意英语的使用。我作为程序员兼英语老师,对这个特别有发言权。
程序员为了赶时间,随便就取了个名字,殊不知拼写错了。我遇到的最夸张的是,有人把Exists(存在)写成了Exit(退出)。这个小小的错误足足耽误了我半天时间,我不仅需要反复确认我的判断,还需要修改很多代码(他的这个错误到处存在)。我开玩笑说,我接手的代码到处都存在(Exist)bug,我都想退出(Exit)了。
我们都知道写错别字是不对的,但是程序员们为了赶时间,往往忽视这点。但是这样就给后来的程序员很大的困扰:改还是不改,这是个问题。
还有一类拼写错误,是反复使用一个词的时候,有一次不小心打错了。其实这样的错误是可以避免的。首先,应该避免反复使用同一个字符串。办法就是把同样的字符串放到静态变量里,或者使用枚举。
单词拼错,并不是一个英文好不好的问题。而是,程序员们工作压力太大,没有时间关心代码可读性。这反映出当今中国社会急功近利的浮躁文化。
平时单词经常读错也是导致写代码的时候写错的原因之一,这个只能靠自己了。
顺便提一下,机器学习中被中国人读错最多的就是ensemble这个单词。这个读ang-semble。这个单词CSDN的某讲师也是读错的。
词性错误在国内也特别常见。我看到有人把方法名叫做Imputer,这明显有问题。如果他好好看过scikit-learn的代码,他应该知道Imputer这个词,肯定是个类名(大写)或者module名(小写)。
类和module是一组相关的操作(动作)的集合,类和module解决一个是什么的问题,方法解决一个干什么的问题。比如,有个类叫做水壶那么,他就会有三个方法,接水,倒水,烧水。如果你把三个方法叫做接水器,倒水器,烧水器,是不是很奇怪?
Python当中,方法名可以用名词,而当方法名是名词的时候,他相当于省略了get或者set。比如plt.title(“人口增长”),就是设置title。这个在Java当中,应该使用setTitle了,而title应该是私有的变量。
我们回过来说Imputer这个词,他肯定是个类,而他的主要方法就是impute。
再说说布尔型的变量。他肯定是个形容词,为了可读性,有时可以加上is或者has。比如isnull()函数,has_key()函数。对于数据挖掘的数据,我会分为categorical和continuous,我没有加is,我觉得这已经很清楚了,加is画蛇添足。
接着前面的has,说说第三人称的问题。国内很多人写代码,容易把has写成have,Exists写出Exist。这里为什么要用第三人称呢?这个问题好像我也回答不了。你可以这样理解:The dict has the key ‘a’。但是为什么impute不是imputes?正确的英语应该是:The imputer imputes the data。根据我的观察,如果返回值是布尔型的,函数使用第三人称。我没见过任何一个编码规范这样说,但是大家实际上都是这么做的。
词性 | 适用于 |
---|---|
名词 | 类名,module名,变量名,方法名 |
动词 | 方法名 |
第三人称动词 | 方法名,返回值是布尔型 |
形容词 | 布尔型变量名,布尔型函数名 |
下表给出python中各种成员的命名语法(grammar):
python成员 | 英语语法 | 举例 |
---|---|---|
模块 Module | 小写名词,可能是抽象名词-tion,动名词 -ing等 | decomposition,classification, preprocessing, processing |
类 class | 大写名词 | Imputer, XGBClassifier, Processor |
变量 variable | 名词 | data, x, y, classifier, n_components, processor(Processor类的实例) |
函数 function | 小写动词,或者名词(和java比相当于省略了get,set) | title(str), fit(X,y), train(X, y), predict(X), initialize, process |
布尔型变量 boolean | 形容词,第三人称动词,用can, is开头 | categoricial, survived, can_survive, passable, mutable, initialized, processed |
布尔型函数 boolean | 形容词,第三人称动词,用can,is开头,用has,exists等第三人称动词开头 | categoricial(), survived(), can_survive(), passable(), isnull(), has_key(str), exists(), isprocessed() |
说完了第三人称里的s,再说说单复数里的s。
单复数也是我们中国人常见的错误。单复数的误用,会对理解程序造成很大的障碍。我经常遇到,数组变量是个单数词。
一般单复数不同形的,数组里应该使用负数形式。单复数同形的,可以加上后缀以示区别。如SheepList等。
示例:
#错误的示范
for e in emplopyee:
print e.name
#正确的示范
for emplopyee in emplopyees:
print emplopyee.name
以上代码第一段中,因为已经用单数做了集合的名字,遍历的时候,只能用一个字母e表示employee了。这个逻辑翻译一下就是:一个员工包含n个员工。这个逻辑怎么解释都不对吧!
不过,在机器学习代码中,很多时候用的是矩阵,不需要遍历和循环的代码,很多人也就不区分单数和复数了。这样也是可以的,一些国外大学教授也是这样写的。
缩写是很伤可读性的。一般形况,应该尽量避免。缩写应该只限定在常用的,大家能理解的词。而且,缩写应该符合语言的规范。缩写,这里指英文缩写。分为两种,首字母缩写词(Acronym)和其他缩写词。常用的首字母缩写词,都是可以用的,如URL,HTML,IO等。而其他的缩写词,最好不要用了。如Department可以写成Dpt,也可以写成Dept,那如果你要用缩写,就一点规范也没有了。你知道Dpt,我也知道,但是换个人呢,他还知道Dpt是Department么?不过,也不是所有的都不能用。出现ID的地方,你还非得用ID不可,你如果用Identity,会很奇怪的。还有OK也是可以用的,这个全世界人民都知道,连不认识字的老太太都知道。我个人觉得,像Dpt这种字典里查不出来的,就不要用了。
首字母缩写词(Acronym),在使用的时候,不要自己造词!我就见过,有人把中国人民银行缩写成了PBOC。我当时一看,就感觉错了。我心想,你还是大陆人么? People’s Republic of China和People’s Bank of China,全称结构一样的,差了一个词而已。前者的简称,都应该知道是PRC吧,推导一下,就知道后者的简称应该是PBC了。那,我是不是马上给他改了呢?不,如果我贸然改了,我也是自己造词。怎么办?这个时候,需要去央行的网站看一下,看看官方说法。我查了官网,确定我是对的,才给他改过来的。
缩写前 | 缩写后 |
---|---|
standard deviation | std |
statistics | stats |
numpy | np |
pandas | pd |
scipy | sp |
matplotlib.plot | plt |
Linear algebra | linalg |
在scikit-learn,scipy,pyspark等的文档中,你看到的一定是。
import numpy as np
import scipy as sp
import pandas as pd
import matplotlib as mpl
import matplotlib.plot as plt
在stackoverflow等网站上,有时候,别人已经默认你认识np,pd等了。他们会给一段代码而不是全部代码,如果你不知道这样的约定熟成,你就不知道他们在说什么。反过来,如果你自己的用法和别人不一样。想象一下吧,你遇到问题了,把代码贴到了StackOverflow,你把numpy缩写成了npy(某csdn老师这样做的),别人一下子可能反应不过来,看不懂,就不来回答你的问题了。这时候你的损失就大了。
(顺便说一句,numpy念num-[pai],而不是csdn某讲师说的num-[pi],scipy也是念[pai]。是python的[pai],munpy和scipy故意用py结尾,表示他们是python库的,却被国内的大学老师念成了放屁的屁…)
写代码和说英语是一样的。别人怎么说英语,你就怎么说。别人怎么写代码,你就怎么写代码。阿里巴巴(alibaba)可以叫阿里(ali),但是你说一个阿里巴(aliba)试试,别人一定不知道你说什么。
词不达意,也分不同的情况。误用了意思有欠准确的英语单词是一种。还有一种,可能只表达了部分的意思。如一个函数,有两个功能。这时,已经不是一个简单的命名的问题了。最好把这个函数分成两个。请记住单一功能原则(Single responsibility principle)。
写一段代码,好比做一篇论文。用词一定要精准。我们写文章的时候,通篇都do,make之类的词,肯定会被英语老师纠正了。程序员最喜欢用的词就是run,process,你要跑什么,你要处理什么?我最熟悉的还是sklearn,谁看到sklearn里面有个run函数了?
如果是程序的主函数,却不知道怎么命名,就命名为main。如果不是主函数,最好按照功能命名。是训练模型就叫train(),预测就叫predict(),可以按照最终的结果命名为generate_report()。
总之,越精准越清楚越好。要是用中文,你肯定就精准了。用英文的难点在于不知道单词,这时候,就要去查字典了。
我以前写过一个函数,叫ChangeName。后来,我发现这样写不对,改成了Rename。同样的一个问题是,别人来找重命名这部分的代码的时候,十之八九,会用Rename这个关键词。我不能让别人找不到吧。
机器学习有一套自己的属于,开发机器学习算法,应该使用这些属于,而不是另搞一套。
单词 | 含义 |
---|---|
fit | 拟合,动词,通常训练模型用fit这个词 |
transform | 变形,动词,通常指消费训练好的模型 |
predict | 预测,动词 |
classify | 分类,动词,可用作方法名,但是我没见人用过,通常训练模型用fit这个词 |
classifier | 分类器,名词,一般用作module或者类名 |
label | 标签,表示数据属于哪个分类 |
labeling | 标签方案,表示分类的结果 |
cluster | 聚类,名词或动词。名词表示一个聚类,动词表示聚类的过程。同样,没见人用过,因为scikit-learn和spark都用fit代替了。 |
clustering | 聚类,名词。1、表示聚类的过程。2、表示聚类的结果,相当于了labeling |
Regressor | 回归器,名词,类名(大写)或者module名(小写) |
estimator | classifier, regressor, clusterer都叫estimator,虽然estimate也有评价的意思,但是机器学习中,对模型的评价是evaluate |
evaluate | 评价模型,方法名 |
metrics | 衡量模型,和evaluation差不多 |
无论是classifier,clusterer,regressor,都用作后缀,区分算法的目的。有些算法可以用于多种目的,就需要后缀。比如PassiveAggressive算法,既可以分类,也可以回归,这是就需要分别命名为PassiveAggressiveClassifier和PassiveAggressiveRegressor。如果算法只有一种用途,则不需要后缀,如kmeans,meanshift,naive bayes。
用x和y表示自变量(independent variable)和应变量(dependent variable)。
这里,我们通常会override变量的大小写规则,你看到大写的X,那并不是全局变量或者类名。大写的X表示矩阵,小写的x表示scalar value (单一值)或者矢量vector。通常机器学习算法中,X应该是一个矩阵,大写,y则是一个vector,小写。
有些情况,y可能是大写的。比如,回归模型中,可能同时计算几个值。我最近训练了一个模型,预测代码运行的时间,我把代码分成了三部分,分开计算时间,这样我的应变量就是t1,t2和t3三个矢量(vector),它们三个组成的应变量就是一个矩阵Y。
另一个情况是,有的时候,多分类问题中的类型是用一个矢量表示的。比如著名的iris数据集,你可以用0,1,2表示。也可以用[1, 0, 0], [0, 1, 0], [0, 0, 1]。后者的应变量就成了矩阵而不是矢量了。
为什么这里可以用大写的变量名?其实Python官方编码规范明确说了,如果编码规范影响了可读性,不必遵守编码规范。
既然提到矩阵,顺便说一句,能用矩阵的地方,尽量用矩阵。否则,很多的循环看着很累,很丑。
尽量使用明确的shape。Python中有一种数组,叫rank one array。这种数组的既不是行向量(row vector),也不是列向量(column vector)。更糟糕的是,他的转置(transpose)和他本身是一样的,这样在矩阵运算的时候就很容易出错了。这是可以用reshape函数使他的shape固定。
rank_one_array = np.random.rand(5)
rank_one_array
Out[20]: array([ 0.3705126 , 0.57665793, 0.88701945, 0.94332253, 0.58982074])
rank_one_array.shape
Out[21]: (5L,)
rank_one_array.T
Out[22]: array([ 0.3705126 , 0.57665793, 0.88701945, 0.94332253, 0.58982074])
reshaped_array = rank_one_array.reshape((1,5))
reshaped_array
Out[24]: array([[ 0.3705126 , 0.57665793, 0.88701945, 0.94332253, 0.58982074]])
reshaped_array.T
Out[25]:
array([[ 0.3705126 ],
[ 0.57665793],
[ 0.88701945],
[ 0.94332253],
[ 0.58982074]])
reshaped_array.shape
Out[26]: (1L, 5L)
算法在训练之前,仅仅是算法,而不叫模型。训练之后,才是模型。我们要保存的,是模型,而不是算法。
这一点,我觉得spark做的特别好。每训练一次,都返回一个模型,而且他们是不同的class,他们的uid是不一样的。反观scikit-learn,他把两者混淆了,容易引起误会。
其实,英文世界中有很多优秀且免费的学习资料。只要你一直是跟着最优秀的人学习,什么代码规范之类的问题,就在潜移默化中学会了,无需特别的学习。
使用Google可以最快的找到问题的答案。使用YouTube可以找到很多人做的学习视频,用几分钟为你把一个概念说清楚。如果你要系统的学习一个领域的知识,Coursera的课程是不错的选择,上面也有中文课程。感觉Coursera的课程把关上面,确实比CSDN好了不止十倍。
Google虽然被墙了,但是可以通过这个地址访问:
http://ac.scmor.com/
YouTube你要自己想办法了。
Coursera和政治没什么关系,但是不知道为什么它的视频也无法正常播放,百度上面有方法,花几分钟就可以彻底解决:
https://jingyan.baidu.com/article/6f2f55a14059eeb5b93e6cab.html
书,还是要看国外的。中国人比较浮躁,“我的程序能跑就行了”。中国人官本位太浓,优秀的程序员都去做官了。中国人也缺少匠人精神,写代码,只是完成任务…