python函数代码优化 python代码性能优化

如何提高python的运行效率窍门一python函数代码优化:关键代码使用外部功能包
Python简化了许多编程任务python函数代码优化,但是对于一些时间敏感python函数代码优化的任务python函数代码优化,它的表现经常不尽人意 。使用C/C或机器语言的外部功能包处理时间敏感任务,可以有效提高应用的运行效率 。这些功能包往往依附于特定的平台 , 因此python函数代码优化你要根据自己所用的平台选择合适的功能包 。简而言之,这个窍门要你牺牲应用的可移植性以换取只有通过对底层主机的直接编程才能获得的运行效率 。以下是一些你可以选择用来提升效率的功能包:
Cython
Pylnlne
PyPy
Pyrex
这些功能包的用处各有不同 。比如说,使用C语言的数据类型,可以使涉及内存操作的任务更高效或者更直观 。Pyrex就能帮助Python延展出这样的功能 。Pylnline能使你在Python应用中直接使用C代码 。内联代码是独立编译的,但是它把所有编译文件都保存在某处,并能充分利用C语言提供的高效率 。
窍门二:在排序时使用键
Python含有许多古老的排序规则,这些规则在你创建定制的排序方法时会占用很多时间 , 而这些排序方法运行时也会拖延程序实际的运行速度 。最佳的排序方法其实是尽可能多地使用键和内置的sort()方法 。譬如,拿下面的代码来说:
import operator
somelist = [(1, 5, , (6, 2, 4), (9, 7, 5)]
somelist.sort(key=operator.itemgetter(0))
somelist
#Output = [(1, 5, , (6, 2, 4), (9, 7, 5)]
somelist.sort(key=operator.itemgetter(1))
somelist
#Output = [(6, 2, 4), (1, 5, , (9, 7, 5)]
somelist.sort(key=operator.itemgetter(2))
somelist
#Output = [(6, 2, 4), (9, 7, 5), (1, 5, ]
在每段例子里,list都是根据你选择的用作关键参数的索引进行排序的 。这个方法不仅对数值类型有效,还同样适用于字符串类型 。
窍门三:针对循环的优化
每一种编程语言都强调最优化的循环方案 。当使用Python时,你可以借助丰富的技巧让循环程序跑得更快 。然而 , 开发者们经常遗忘的一个技巧是:尽量避免在循环中访问变量的属性 。譬如,拿下面的代码来说:
lowerlist = ['this', 'is', 'lowercase']
upper = str.upper
upperlist = []
append = upperlist.append
for word in lowerlist:
append(upper(word))
print(upperlist)
#Output = ['THIS', 'IS', 'LOWERCASE']
【python函数代码优化 python代码性能优化】每次你调用str.upper, Python都会计算这个式子的值 。然而,如果你把这个求值赋值给一个变量,那么求值的结果就能提前知道,Python程序就能运行得更快 。因此,关键就是尽可能减小Python在循环中的工作量 。因为Python解释执行的特性,在上面的例子中会大大减慢它的速度 。
(注意:优化循环的方法还有很多,这只是其中之一 。比如,很多程序员会认为,列表推导式是提高循环速度的最佳方法 。关键在于,优化循环方案是提高应用程序运行速度的上佳选择 。)
窍门四:使用较新的Python版本
如果你在网上搜索Python,你会发现数不尽的信息都是关于如何升级Python版本 。通常,每个版本的Python都会包含优化内容,使其运行速度优于之前的版本 。但是,限制因素在于,你最喜欢的函数库有没有同步更新支持新的Python版本 。与其争论函数库是否应该更新,关键在于新的Python版本是否足够高效来支持这一更新 。
你要保证自己的代码在新版本里还能运行 。你需要使用新的函数库才能体验新的Python版本 , 然后你需要在做出关键性的改动时检查自己的应用 。只有当你完成必要的修正之后,你才能体会新版本的不同 。
然而,如果你只是确保自己的应用在新版本中可以运行 , 你很可能会错过新版本提供的新特性 。一旦你决定更新,请分析你的应用在新版本下的表现 , 并检查可能出问题的部分,然后优先针对这些部分应用新版本的特性 。只有这样,用户才能在更新之初就觉察到应用性能的改观 。
窍门五:尝试多种编码方法
每次创建应用时都使用同一种编码方法几乎无一例外会导致应用的运行效率不尽人意 。可以在程序分析时尝试一些试验性的办法 。譬如说,在处理字典中的数据项时,你既可以使用安全的方法,先确保数据项已经存在再进行更新,也可以直接对数据项进行更新,把不存在的数据项作为特例分开处理 。请看下面第一段代码:
n = 16
myDict = {}
for i in range(0, n):
char = 'abcd'[i%4]
if char not in myDict:
myDict[char] = 0
myDict[char]= 1
print(myDict)
当一开始myDict为空时 , 这段代码会跑得比较快 。然而 , 通常情况下 , myDict填满了数据,至少填有大部分数据 , 这时换另一种方法会更有效率 。
n = 16
myDict = {}
for i in range(0, n):
char = 'abcd'[i%4]
try:
myDict[char]= 1
except KeyError:
myDict[char] = 1
print(myDict)
在两种方法中输出结果都是一样的 。区别在于输出是如何获得的 。跳出常规的思维模式,创建新的编程技巧能使你的应用更有效率 。
窍门六:交叉编译你的应用
开发者有时会忘记计算机其实并不理解用来创建现代应用程序的编程语言 。计算机理解的是机器语言 。为了运行你的应用 , 你借助一个应用将你所编的人类可读的代码转换成机器可读的代码 。有时,你用一种诸如Python这样的语言编写应用 , 再以C这样的语言运行你的应用,这在运行的角度来说,是可行的 。关键在于,你想你的应用完成什么事情,而你的主机系统能提供什么样的资源 。
Nuitka是一款有趣的交叉编译器,能将你的Python代码转化成C代码 。这样,你就可以在native模式下执行自己的应用,而无需依赖于解释器程序 。你会发现自己的应用运行效率有了较大的提高,但是这会因平台和任务的差异而有所不同 。
(注意:Nuitka现在还处在测试阶段,所以在实际应用中请多加注意 。实际上,当下最好还是把它用于实验 。此外,关于交叉编译是否为提高运行效率的最佳方法还存在讨论的空间 。开发者已经使用交叉编译多年,用来提高应用的速度 。记?。?每一种解决办法都有利有弊,在把它用于生产环境之前请仔细权衡 。)
在使用交叉编译器时,记得确保它支持你所用的Python版本 。Nuitka支持Python2.6, 2.7, 3.2和3.3 。为了让解决方案生效,你需要一个Python解释器和一个C编译器 。Nuitka支持许多C编译器,其中包括Microsoft Visual Studio,MinGW 和 Clang/LLVM 。
交叉编译可能造成一些严重问题 。比如,在使用Nuitka时,你会发现即便是一个小程序也会消耗巨大的驱动空间 。因为Nuitka借助一系列的动态链接库(DDLs)来执行Python的功能 。因此 , 如果你用的是一个资源很有限的系统,这种方法或许不太可行 。
「干货」让Python性能起飞的15个技巧,你知道几个呢?前言
Python 一直以来被大家所诟病的一点就是执行速度慢,但不可否认的是 Python 依然是我们学习和工作中的一大利器 。本文总结了15个tips有助于提升 Python 执行速度、优化性能 。
关于 Python 如何精确地测量程序的执行时间,这个问题看起来简单其实很复杂,因为程序的执行时间受到很多因素的影响,例如操作系统、Python 版本以及相关硬件(CPU 性能、内存读写速度)等 。在同一台电脑上运行相同版本的语言时 , 上述因素就是确定的了,但是程序的睡眠时间依然是变化的,且电脑上正在运行的其他程序也会对实验有干扰,因此严格来说这就是实验不可重复 。
我了解到的关于计时比较有代表性的两个库就是time和timeit。
其中,time库中有time()、perf_counter()以及process_time()三个函数可用来计时(以秒为单位),加后缀_ns表示以纳秒计时(自 Python3.7 始) 。在此之前还有clock()函数 , 但是在 Python3.3 之后被移除了 。上述三者的区别如下:
与time库相比,timeit有两个优点:
timeit.timeit(stmt='pass', setup='pass', timer= , number=1000000, globals=None)参数说明:
本文所有的计时均采用timeit方法,且采用默认的执行次数一百万次 。
为什么要执行一百万次呢?因为我们的测试程序很短 , 如果不执行这么多次的话,根本看不出差距 。
Exp1:将字符串数组中的小写字母转为大写字母 。
测试数组为 oldlist = ['life', 'is', 'short', 'i', 'choose', 'python'] 。
方法一
方法二
方法一耗时0.5267724000000005s,方法二耗时0.41462569999999843s , 性能提升21.29%
Exp2:求两个list的交集 。
测试数组:a = [1,2,3,4,5],b = [2,4,6,8,10] 。
方法一
方法二
方法一耗时0.9507264000000006s , 方法二耗时0.6148200999999993s,性能提升35.33%
关于set()的语法:|、、-分别表示求并集、交集、差集 。
我们可以通过多种方式对序列进行排序,但其实自己编写排序算法的方法有些得不偿失 。因为内置的sort()或sorted()方法已经足够优秀了 , 且利用参数key可以实现不同的功能,非常灵活 。二者的区别是sort()方法仅被定义在list中,而sorted()是全局方法对所有的可迭代序列都有效 。
Exp3:分别使用快排和sort()方法对同一列表排序 。
测试数组:lists = [2,1,4,3,0] 。
方法一
方法二
方法一耗时2.4796975000000003s ,方法二耗时0.05551999999999424s ,性能提升97.76%
顺带一提 , sorted()方法耗时0.1339823999987857s。
可以看出,sort()作为list专属的排序方法还是很强的,sorted()虽然比前者慢一点,但是胜在它“不挑食”,它对所有的可迭代序列都有效 。
扩展 :如何定义sort()或sorted()方法的key
1.通过lambda定义
2.通过operator定义
operator的itemgetter()适用于普通数组排序,attrgetter()适用于对象数组排序
3.通过cmp_to_key()定义,最为灵活
Exp4:统计字符串中每个字符出现的次数 。
测试数组:sentence='life is short, i choose python' 。
方法一
方法二
方法一耗时2.8105250000000055s ,方法二耗时1.6317423000000062s,性能提升41.94%
列表推导(list comprehension)短小精悍 。在小代码片段中,可能没有太大的区别 。但是在大型开发中,它可以节省一些时间 。
Exp5:对列表中的奇数求平方,偶数不变 。
测试数组:oldlist = range(10) 。
方法一
方法二
方法一耗时1.5342976000000021s ,方法二耗时1.4181957999999923s,性能提升7.57%
大多数人都习惯使用来连接字符串 。但其实 , 这种方法非常低效 。因为,操作在每一步中都会创建一个新字符串并复制旧字符串 。更好的方法是用join()来连接字符串 。关于字符串的其他操作,也尽量使用内置函数 , 如isalpha()、isdigit()、startswith()、endswith()等 。
Exp6:将字符串列表中的元素连接起来 。
测试数组:oldlist = ['life', 'is', 'short', 'i', 'choose', 'python'] 。
方法一
方法二
方法一耗时0.27489080000000854s,方法二耗时0.08166570000000206s ,性能提升70.29%
join还有一个非常舒服的点,就是它可以指定连接的分隔符,举个例子
life//is//short//i//choose//python
Exp6:交换x,y的值 。
测试数据:x, y = 100, 200 。
方法一
方法二
方法一耗时0.027853900000010867s , 方法二耗时0.02398730000000171s,性能提升13.88%
在不知道确切的循环次数时,常规方法是使用while True进行无限循环,在代码块中判断是否满足循环终止条件 。虽然这样做没有任何问题,但while 1的执行速度比while True更快 。因为它是一种数值转换,可以更快地生成输出 。
Exp8:分别用while 1和while True循环 100 次 。
方法一
方法二
方法一耗时3.679268300000004s,方法二耗时3.607847499999991s,性能提升 1.94%
将文件存储在高速缓存中有助于快速恢复功能 。Python 支持装饰器缓存,该缓存在内存中维护特定类型的缓存,以实现最佳软件驱动速度 。我们使用lru_cache装饰器来为斐波那契函数提供缓存功能,在使用fibonacci递归函数时,存在大量的重复计算,例如fibonacci(1)、fibonacci(2)就运行了很多次 。而在使用了lru_cache后,所有的重复计算只会执行一次 , 从而大大提高程序的执行效率 。
Exp9:求斐波那契数列 。
测试数据:fibonacci(7) 。
方法一
方法二
方法一耗时3.955014900000009s ,方法二耗时0.05077979999998661s,性能提升98.72%
注意事项:
我被执行了(执行了两次demo(1, 2),却只输出一次)
functools.lru_cache(maxsize=128, typed=False)的两个可选参数:
点运算符(.)用来访问对象的属性或方法 , 这会引起程序使用__getattribute__()和__getattr__()进行字典查找,从而带来不必要的开销 。尤其注意,在循环当中,更要减少点运算符的使用 , 应该将它移到循环外处理 。
这启发我们应该尽量使用from ... import ...这种方式来导包,而不是在需要使用某方法时通过点运算符来获取 。其实不光是点运算符,其他很多不必要的运算我们都尽量移到循环外处理 。
Exp10:将字符串数组中的小写字母转为大写字母 。
测试数组为 oldlist = ['life', 'is', 'short', 'i', 'choose', 'python'] 。
方法一
方法二
方法一耗时0.7235491999999795s , 方法二耗时0.5475435999999831s , 性能提升24.33%
当我们知道具体要循环多少次时 , 使用for循环比使用while循环更好 。
Exp12:使用for和while分别循环 100 次 。
方法一
方法二
方法一耗时3.894683299999997s ,方法二耗时1.0198077999999953s,性能提升 73.82%
Numba 可以将 Python 函数编译码为机器码执行,大大提高代码执行速度 , 甚至可以接近 C 或 FORTRAN 的速度 。它能和 Numpy 配合使用,在 for 循环中或存在大量计算时能显著地提高执行效率 。
Exp12:求从 1 加到 100 的和 。
方法一
方法二
方法一耗时3.7199997000000167s ,方法二耗时0.23769430000001535s ,性能提升93.61%
矢量化是 NumPy 中的一种强大功能,可以将操作表达为在整个数组上而不是在各个元素上发生 。这种用数组表达式替换显式循环的做法通常称为矢量化 。
在 Python 中循环数组或任何数据结构时,会涉及很多开销 。NumPy 中的向量化操作将内部循环委托给高度优化的 C 和 Fortran 函数 , 从而使 Python 代码更加快速 。
Exp13:两个长度相同的序列逐元素相乘 。
测试数组:a = [1,2,3,4,5], b = [2,4,6,8,10]
方法一
方法二
方法一耗时0.6706845000000214s,方法二耗时0.3070132000000001s , 性能提升54.22%
若要检查列表中是否包含某成员 , 通常使用in关键字更快 。
Exp14:检查列表中是否包含某成员 。
测试数组:lists = ['life', 'is', 'short', 'i', 'choose', 'python']
方法一
方法二
方法一耗时0.16038449999999216s ,方法二耗时0.04139250000000061s ,性能提升74.19%
itertools是用来操作迭代器的一个模块,其函数主要可以分为三类:无限迭代器、有限迭代器、组合迭代器 。
Exp15:返回列表的全排列 。
测试数组:["Alice", "Bob", "Carol"]
方法一
方法二
方法一耗时3.867292899999484s , 方法二耗时0.3875405000007959s , 性能提升89.98%
根据上面的测试数据,我绘制了下面这张实验结果图,可以更加直观的看出不同方法带来的性能差异 。
从图中可以看出,大部分的技巧所带来的性能增幅还是比较可观的 , 但也有少部分技巧的增幅较?。ɡ绫嗪?、7、8,其中,第 8 条的两种方法几乎没有差异) 。
总结下来,我觉得其实就是下面这两条原则:
内置库函数由专业的开发人员编写并经过了多次测试,很多库函数的底层是用C语言开发的 。因此 , 这些函数总体来说是非常高效的(比如sort()、join()等),自己编写的方法很难超越它们,还不如省省功夫 , 不要重复造轮子了,何况你造的轮子可能更差 。所以 , 如果函数库中已经存在该函数 , 就直接拿来用 。
有很多优秀的第三方库,它们的底层可能是用 C 和 Fortran 来实现的,像这样的库用起来绝对不会吃亏 , 比如前文提到的 Numpy 和 Numba,它们带来的提升都是非常惊人的 。类似这样的库还有很多 , 比如Cython、PyPy等,这里我只是抛砖引玉 。
原文链接:
Python怎么做最优化最优化
为什么要做最优化呢?因为在生活中,人们总是希望幸福值或其它达到一个极值,比如做生意时希望成本最小 , 收入最大 , 所以在很多商业情境中,都会遇到求极值的情况 。
函数求根
这里「函数的根」也称「方程的根」 , 或「函数的零点」 。
先把我们需要的包加载进来 。import numpy as npimport scipy as spimport scipy.optimize as optimport matplotlib.pyplot as plt%matplotlib inline
函数求根和最优化的关系?什么时候函数是最小值或最大值?
两个问题一起回答:最优化就是求函数的最小值或最大值,同时也是极值,在求一个函数最小值或最大值时 , 它所在的位置肯定是导数为 0 的位置 , 所以要求一个函数的极值,必然要先求导,使其为 0,所以函数求根就是为了得到最大值最小值 。
scipy.optimize 有什么方法可以求根?
可以用 scipy.optimize 中的 bisect 或 brentq 求根 。f = lambda x: np.cos(x) - x # 定义一个匿名函数x = np.linspace(-5, 5, 1000) # 先生成 1000 个 xy = f(x) # 对应生成 1000 个 f(x)plt.plot(x, y); # 看一下这个函数长什么样子plt.axhline(0, color='k'); # 画一根横线 , 位置在 y=0
opt.bisect(f, -5, 5) # 求取函数的根0.7390851332155535plt.plot(x, y)plt.axhline(0, color='k')plt.scatter([_], [0], c='r', s=100); # 这里的 [_] 表示上一个 Cell 中的结果 , 这里是 x 轴上的位置,0 是 y 上的位置
求根有两种方法,除了上面介绍的 bisect,还有 brentq,后者比前者快很多 。%timeit opt.bisect(f, -5, 5)%timeit opt.brentq(f, -5, 5)10000 loops, best of 3: 157 s per loopThe slowest run took 11.65 times longer than the fastest. This could mean that an intermediate result is being cached.10000 loops, best of 3: 35.9 s per loop
函数求最小化
求最小值就是一个最优化问题 。求最大值时只需对函数做一个转换,比如加一个负号,或者取倒数,就可转成求最小值问题 。所以两者是同一问题 。
初始值对最优化的影响是什么?
举例来说 , 先定义个函数 。f = lambda x: 1-np.sin(x)/xx = np.linspace(-20., 20., 1000)y = f(x)
当初始值为 3 值 , 使用 minimize 函数找到最小值 。minimize 函数是在新版的 scipy 里,取代了以前的很多最优化函数 , 是个通用的接口 , 背后是很多方法在支撑 。x0 = 3xmin = opt.minimize(f, x0).x # x0 是起始点,起始点最好离真正的最小值点不要太远plt.plot(x, y)plt.scatter(x0, f(x0), marker='o', s=300); # 起始点画出来 , 用圆圈表示plt.scatter(xmin, f(xmin), marker='v', s=300); # 最小值点画出来,用三角表示plt.xlim(-20, 20);
初始值为 3 时,成功找到最小值 。
现在来看看初始值为 10 时,找到的最小值点 。x0 = 10xmin = opt.minimize(f, x0).xplt.plot(x, y)plt.scatter(x0, f(x0), marker='o', s=300)plt.scatter(xmin, f(xmin), marker='v', s=300)plt.xlim(-20, 20);
由上图可见 , 当初始值为 10 时,函数找到的是局部最小值点,可见 minimize 的默认算法对起始点的依赖性 。
那么怎么才能不管初始值在哪个位置,都能找到全局最小值点呢?
如何找到全局最优点?
可以使用 basinhopping 函数找到全局最优点,相关背后算法,可以看帮助文件 , 有提供论文的索引和出处 。
我们设初始值为 10 看是否能找到全局最小值点 。x0 = 10from scipy.optimize import basinhoppingxmin = basinhopping(f,x0,stepsize = 5).xplt.plot(x, y);plt.scatter(x0, f(x0), marker='o', s=300);plt.scatter(xmin, f(xmin), marker='v', s=300);plt.xlim(-20, 20);
当起始点在比较远的位置 , 依然成功找到了全局最小值点 。
如何求多元函数最小值?
以二元函数为例,使用 minimize 求对应的最小值 。def g(X): x,y = X return (x-1)**45 * (y-1)**2 - 2*x*yX_opt = opt.minimize(g, (8, 3)).x # (8,3) 是起始点print X_opt[ 1.88292611 1.37658521]fig, ax = plt.subplots(figsize=(6, 4)) # 定义画布和图形x_ = y_ = np.linspace(-1, 4, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, g((X, Y)), 50) # 等高线图ax.plot(X_opt[0], X_opt[1], 'r*', markersize=15) # 最小点的位置是个元组ax.set_xlabel(r"$x_1$", fontsize=18)ax.set_ylabel(r"$x_2$", fontsize=18)plt.colorbar(c, ax=ax) # colorbar 表示颜色越深,高度越高fig.tight_layout()
画3D 图 。from mpl_toolkits.mplot3d import Axes3Dfrom matplotlib import cmfig = plt.figure()ax = fig.gca(projection='3d')x_ = y_ = np.linspace(-1, 4, 100)X, Y = np.meshgrid(x_, y_)surf = ax.plot_surface(X, Y, g((X,Y)), rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)cset = ax.contour(X, Y, g((X,Y)), zdir='z',offset=-5, cmap=cm.coolwarm)fig.colorbar(surf, shrink=0.5, aspect=5);
曲线拟合
曲线拟合和最优化有什么关系?
曲线拟合的问题是 , 给定一组数据,它可能是沿着一条线散布的,这时要找到一条最优的曲线来拟合这些数据,也就是要找到最好的线来代表这些点,这里的最优是指这些点和线之间的距离是最小的 , 这就是为什么要用最优化问题来解决曲线拟合问题 。
举例说明,给一些点,找到一条线 , 来拟合这些点 。
先给定一些点:N = 50 # 点的个数m_true = 2 # 斜率b_true = -1 # 截距dy = 2.0 # 误差np.random.seed(0)xdata = https://www.04ip.com/post/10 * np.random.random(N) # 50 个 x , 服从均匀分布ydata = np.random.normal(b_truem_true * xdata, dy) # dy 是标准差plt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');
上面的点整体上呈现一个线性关系,要找到一条斜线来代表这些点,这就是经典的一元线性回归 。目标就是找到最好的线,使点和线的距离最短 。要优化的函数是点和线之间的距离,使其最小 。点是确定的,而线是可变的,线是由参数值 , 斜率和截距决定的,这里就是要通过优化距离找到最优的斜率和截距 。
点和线的距离定义如下:def chi2(theta, x, y): return np.sum(((y - theta[0] - theta[1] * x)) ** 2)
上式就是误差平方和 。
误差平方和是什么?有什么作用?
误差平方和公式为:
误差平方和大,表示真实的点和预测的线之间距离太远 , 说明拟合得不好,最好的线,应该是使误差平方和最?。醋钣诺哪夂舷撸饫锸翘踔毕?。
误差平方和就是要最小化的目标函数 。
找到最优的函数,即斜率和截距 。theta_guess = [0, 1] # 初始值theta_best = opt.minimize(chi2, theta_guess, args=(xdata, ydata)).xprint(theta_best)[-1.01442005 1.93854656]
上面两个输出即是预测的直线斜率和截距,我们是根据点来反推直线的斜率和截距 , 那么真实的斜率和截距是多少呢?-1 和 2,很接近了,差的一点是因为有噪音的引入 。xfit = np.linspace(0, 10)yfit = theta_best[0]theta_best[1] * xfitplt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');plt.plot(xfit, yfit, '-k');
最小二乘(Least Square)是什么?
上面用的是 minimize 方法 , 这个问题的目标函数是误差平方和 , 这就又有一个特定的解法 , 即最小二乘 。
最小二乘的思想就是要使得观测点和估计点的距离的平方和达到最小 , 这里的“二乘”指的是用平方来度量观测点与估计点的远近(在古汉语中“平方”称为“二乘”),“最小”指的是参数的估计值要保证各个观测点与估计点的距离的平方和达到最小 。
关于最小二乘估计的计算,涉及更多的数学知识,这里不想详述,其一般的过程是用目标函数对各参数求偏导数 , 并令其等于 0,得到一个线性方程组 。具体推导过程可参考斯坦福机器学习讲义 第 7 页 。def deviations(theta, x, y): return (y - theta[0] - theta[1] * x)theta_best, ier = opt.leastsq(deviations, theta_guess, args=(xdata, ydata))print(theta_best)[-1.01442016 1.93854659]
最小二乘 leastsq 的结果跟 minimize 结果一样 。注意 leastsq 的第一个参数不再是误差平方和 chi2,而是误差本身 deviations , 即没有平方,也没有和 。yfit = theta_best[0]theta_best[1] * xfitplt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');plt.plot(xfit, yfit, '-k');
非线性最小二乘
上面是给一些点,拟合一条直线,拟合一条曲线也是一样的 。def f(x, beta0, beta1, beta2): # 首先定义一个非线性函数,有 3 个参数 return beta0beta1 * np.exp(-beta2 * x**2)beta = (0.25, 0.75, 0.5) # 先猜 3 个 betaxdata = https://www.04ip.com/post/np.linspace(0, 5, 50)y = f(xdata, *beta)ydata = y0.05 * np.random.randn(len(xdata)) # 给 y 加噪音def g(beta): return ydata - f(xdata, *beta) # 真实 y 和 预测值的差,求最优曲线时要用到beta_start = (1, 1, 1)beta_opt, beta_cov = opt.leastsq(g, beta_start)print beta_opt # 求到的 3 个最优的 beta 值[ 0.25525709 0.74270226 0.54966466]
拿估计的 beta_opt 值跟真实的 beta = (0.25, 0.75, 0.5) 值比较,差不多 。fig, ax = plt.subplots()ax.scatter(xdata, ydata) # 画点ax.plot(xdata, y, 'r', lw=2) # 真实值的线ax.plot(xdata, f(xdata, *beta_opt), 'b', lw=2) # 拟合的线ax.set_xlim(0, 5)ax.set_xlabel(r"$x$", fontsize=18)ax.set_ylabel(r"$f(x, \beta)$", fontsize=18)fig.tight_layout()
除了使用最小二乘,还可以使用曲线拟合的方法 , 得到的结果是一样的 。beta_opt, beta_cov = opt.curve_fit(f, xdata, ydata)print beta_opt[ 0.25525709 0.74270226 0.54966466]
有约束的最小化
有约束的最小化是指,要求函数最小化之外,还要满足约束条件,举例说明 。
边界约束def f(X): x, y = X return (x-1)**2(y-1)**2 # 这是一个碗状的函数x_opt = opt.minimize(f, (0, 0), method='BFGS').x # 无约束最优化
假设有约束条件,x 和 y 要在一定的范围内 , 如 x 在 2 到 3 之间,y 在 0 和 2 之间 。bnd_x1, bnd_x2 = (2, 3), (0, 2) # 对自变量的约束x_cons_opt = opt.minimize(f, np.array([0, 0]), method='L-BFGS-B', bounds=[bnd_x1, bnd_x2]).x # bounds 矩形约束fig, ax = plt.subplots(figsize=(6, 4))x_ = y_ = np.linspace(-1, 3, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, f((X,Y)), 50)ax.plot(x_opt[0], x_opt[1], 'b*', markersize=15) # 没有约束下的最小值,蓝色五角星ax.plot(x_cons_opt[0], x_cons_opt[1], 'r*', markersize=15) # 有约束下的最小值 , 红色星星bound_rect = plt.Rectangle((bnd_x1[0], bnd_x2[0]), bnd_x1[1] - bnd_x1[0], bnd_x2[1] - bnd_x2[0], facecolor="grey")ax.add_patch(bound_rect)ax.set_xlabel(r"$x_1$", fontsize=18)ax.set_ylabel(r"$x_2$", fontsize=18)plt.colorbar(c, ax=ax)fig.tight_layout()
不等式约束
介绍下相关理论,先来看下存在等式约束的极值问题求法 , 比如下面的优化问题 。
目标函数是 f(w),下面是等式约束,通常解法是引入拉格朗日算子,这里使用 ββ 来表示算子 , 得到拉格朗日公式为
l 是等式约束的个数 。
然后分别对 w 和ββ 求偏导,使得偏导数等于 0,然后解出 w 和βiβi,至于为什么引入拉格朗日算子可以求出极值,原因是 f(w) 的 dw 变化方向受其他不等式的约束,dw的变化方向与f(w)的梯度垂直时才能获得极值,而且在极值处,f(w) 的梯度与其他等式梯度的线性组合平行 , 因此他们之间存在线性关系 。(参考《最优化与KKT条件》)
对于不等式约束的极值问题
常常利用拉格朗日对偶性将原始问题转换为对偶问题,通过解对偶问题而得到原始问题的解 。该方法应用在许多统计学习方法中 。有兴趣的可以参阅相关资料 , 这里不再赘述 。def f(X): return (X[0] - 1)**2(X[1] - 1)**2def g(X): return X[1] - 1.75 - (X[0] - 0.75)**4x_opt = opt.minimize(f, (0, 0), method='BFGS').xconstraints = [dict(type='ineq', fun=g)] # 约束采用字典定义,约束方式为不等式约束 , 边界用 g 表示x_cons_opt = opt.minimize(f, (0, 0), method='SLSQP', constraints=constraints).xfig, ax = plt.subplots(figsize=(6, 4))x_ = y_ = np.linspace(-1, 3, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, f((X, Y)), 50)ax.plot(x_opt[0], x_opt[1], 'b*', markersize=15) # 蓝色星星,没有约束下的最小值ax.plot(x_, 1.75(x_-0.75)**4, '', markersize=15)ax.fill_between(x_, 1.75(x_-0.75)**4, 3, color="grey")ax.plot(x_cons_opt[0], x_cons_opt[1], 'r*', markersize=15) # 在区域约束下的最小值ax.set_ylim(-1, 3)ax.set_xlabel(r"$x_0$", fontsize=18)ax.set_ylabel(r"$x_1$", fontsize=18)plt.colorbar(c, ax=ax)fig.tight_layout()
scipy.optimize.minimize 中包括了多种最优化算法,每种算法使用范围不同 , 详细参考官方文档 。
这段python代码如何优化?第一:不需要定义main函数python函数代码优化,直接写就好 。
第二:代码python函数代码优化的逻辑也是有问题的 。一般来说,按这样的框架去写:
_size,_verbs, A, s = [int(_) for _ in imput().split(' ')], [], ''
for _ in range(_size):A.append(...)# 读入矩阵所有行
def G:
....实现特征函数G
for _ in range(_verbs):
....verb = [int(_) for _ in imput().split(' ')]
.... if verb[0] = 1:
........行翻转
....elif verb[0] = 2:
........列翻转
....else:
........s= G()
print(s)
关于python函数代码优化和python代码性能优化的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读