您的位置:首页 > 编程语言 > Python开发

python程序的优化经验

2015-12-24 19:44 597 查看
经验一:

在机器学习算法中,有时候会写多重for循环,在多重循环的循环体内会调用某些工具函数。这时候,工具函数的效率就非常关键,因为工具函数经常会被调用100w+次。如果工具函数多执行10000次,那么就要整个for循环跑完就要多执行100亿次。

userIdList = normUserCountVectDict.keys()
actionTypeList = ['0','2','3']
fieldList = ['cat_id','seller_id','brand_id','item_id']

#建立行为的下标映射,方便存储相似性得分
fieldIndex = {}
index = 0
for action in actionTypeList:
for field in fieldList:
fieldIndex[action+'#'+field] = index
index += 1
#这个循环体必须得优化
#因为userIdList的长度是3w+
#循环最多次的循环放在最内层
for classNum in classNumList:
for actionType in actionTypeList:
for field in fieldList:
normClassVec = normClassCountVectDict[classNum][actionType][field]
index = fieldIndex[actionType+'#'+field]
for userId in userIdList:
normUserVec = normUserCountVectDict[userId][actionType][field]
#有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
if len(normUserVec) == 0:
similarity[classNum][userId][index] = 0
else:
similarity[classNum][userId][index] = cosSim(normUserVec,normClassVec)


分析:上述4层for循环,第一层12次,第二层3,第三次4次,第四层10w+。总共循环体需要执行1000w+次左右。并且在循环体中,需要调用函数cosSim(normUserVec,normClassVec)。刚开始我没注意到,在cosSim函数里面有些无意义的操作,导致程序非常慢。无意义的操作后来注释掉,程序性能就提高。注释掉的函数vecLength()是比较耗时的,每次只需1000运算,那么1000*1000w=100亿次操作。

def cosSim(dictA,dictB):
#先判断dictA,dictB的模长是否为1
#这个太误差检验太耗时,因为这个函数频繁被调用,上亿次,核心函数
#lengthA = vecLength(dictA)
#lengthB = vecLength(dictB)
#数值计算问题,不适合用lengthA=1来判断是否已归一化
#if (abs(lengthA-1)>0.01) or (abs(lengthB-1)>0.01):
#       print dictA
#       print lengthA
#       print "error:vector not normal"
commonKey = set(dictA.keys()) & set(dictB.keys())
sim = 0.0
for key in commonKey:
sim += dictA[key]*dictB[key]
return sim


经验二:多重for循环,最好把循环次数多的循环放在最内层。并且,如果能在外层循环就计算出结果,就不放到内层循环里面

#这个循环体必须得优化
#因为userIdList的长度是3w+
#循环最多次的循环放在最内层
for classNum in classNumList:
for actionType in actionTypeList:
for field in fieldList:
normClassVec = normClassCountVectDict[classNum][actionType][field]
index = fieldIndex[actionType+'#'+field]
for userId in userIdList:
normUserVec = normUserCountVectDict[userId][actionType][field]
#有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
if len(normUserVec) == 0:
similarity[classNum][userId][index] = 0
else:
similarity[classNum][userId][index] = cosSim(normUserVec,normClassVec)
分析:上述userId的循环次数是最多的,所以放在内层循环。

normClassVec = normClassCountVectDict[classNum][actionType][field]
index = fieldIndex[actionType+'#'+field]


并且,上述两条语句可以在外层循环计算得到结果,就不要放到内层循环里面计算。效率低下。

经验三:

如果需要查询元素达到上亿次,那么就不要把待查询的元素放在list里面,因为list的查询是遍历法,即 if a in list 采用的是遍历的方法去查询,所以,这时候,就要把待查询的元素放在dict或set里面,用空间换时间。

经验四:

在多层循环体内部,最好不要调用函数,因为调用函数需要出栈,入栈操作,会非常耗时间。c++可以使用内联函数,python好像没有这个机制,所以,多重for循环尽量避免调用函数。上面的例子中,多重for循环调用了函数cosSim。是程序性能上的瓶颈,最好不要调用,直接在循环体内实现。

for classNum in classNumList:
for actionType in actionTypeList:
for field in fieldList:
normClassVec = normClassCountVectDict[classNum][actionType][field] index = fieldIndex[actionType+'#'+field]dictAKeys = set(normClassVec.keys())
for userId in userIdList:
normUserVec = normUserCountVectDict[userId][actionType][field]
#有些用户的特征向量会是零向量,比如用户无点击行为,必须考虑这种异常情况
if len(normUserVec) == 0:
similarity[classNum][userId][index] = 0
else:
#内层循环里面不适合调用函数,耗时
dictBKeys = set(normUserVec.keys())
commonKey = dictAKeys & dictBKeys
sim = 0.0
for key in commonKey:
sim += normClassVec[key]*normUserVec[key]
similarity[classNum][userId][index] = sim


解析:上述程序中,将cosSim函数直接在循环体内实现,使性能有了极大的提高,可以看出在循环体内调用函数确实是非常耗时的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: