Python代码中的偏函数
技术背景
在数学中我们都学过偏导数\(\frac{\partial f(x,y)}{\partial x}\),而这里我们提到的偏函数,指的是\(f(y)(x)\)。也就是说,在代码实现的过程中,虽然我们实现的一个函数可能带有很多个变量,但是可以用偏函数的形式把其中一些不需要拆分和变化的变量转变为固有变量。比较典型的两个例子是计算偏导数和多进程优化。虽然大部分支持自动微分的框架都有相应的支持偏导数的接口,多进程操作中也可以指定额外的args,但是这些自带的方法在形式上都是比较tricky的,感觉并不如使用偏函数优雅和简洁。这里我们主要介绍python中可能会用到的偏函数功能--partial
。
Partial简单案例
我们先来一个最简单的乘法函数\(f(x,y)=xy\)。假如说我们想得到该函数关于y的偏导数,注意,这里y是第二个输入的变量,不是第一个位置,一般自动微分框架都默认都第一个位置的变量计算偏导数。相关代码实现如下所示:
from functools import partial
def mul(x, y):
print (locals())
return x * y
x = 2
y = 3
res_0 = mul(x, y)
partial_mul = partial(mul, x=x)
res_1 = partial_mul(y=3)
print ('The result is: {}'.format(res_0))
print ('The result is: {}'.format(res_1))
这段代码的运行结果为:
{'x': 2, 'y': 3}
{'x': 2, 'y': 3}
The result is: 6
The result is: 6
我们现在来分析一下上面这个案例中所体现的信息:
- 在使用partial函数时使用的是关键字参数,即时原本的变量不是一个关键字参数,而是一个位置参数。
- 虽然得到的偏函数partial_mul运行的方式跟函数一致,但其实它是一个partial的对象类型。
- 在生成partial_mul对象时已经执行过一遍函数,因此函数中的打印语句被打印了两次。
- 偏函数的计算结果肯定是跟原函数保持一致的,但是在一些特殊场景下,我们可能会用到这种单变量的偏函数。
Concurrent多核并行场景
现在我们稍微修改一下上面的案例,我们要用concurrent这个并行工具去分别执行上述乘法任务,同时输入的x也变成了一个多维的数组。然后为了验证并行算法,这里每计算一次元素乘法,我们都用time.sleep
方法让进程休眠2秒钟时间。由于此时的参数y还是一个标量,但是每次乘法计算我们都需要输入这个标量,因此我们直接将其封装到一个partial偏函数中,使得函数变成:\(f(x,y)=f(y)(x)=P(x)\),然后对x这个入参进行并行化操作:
import numpy as np
import concurrent.futures
from functools import partial
import time
# 定义休眠函数
def mul(x, y):
time.sleep(2)
return x * y
# 定义入参
x = np.array([1, 2, 3], np.float32)
y = 3.
# 有阻塞计算
time_0 = time.time()
res_0 = []
for _x in x:
res_0.append(mul(_x, y))
res_0 = np.array(res_0, np.float32)
time_1 = time.time()
# 并行计算
partial_mul = partial(mul, y=y)
time_2 = time.time()
with concurrent.futures.ProcessPoolExecutor(max_workers=x.shape[0]) as executor:
res = executor.map(partial_mul, x)
res_1 = np.array(list(res), np.float32)
time_3 = time.time()
print ('The result is: {}, and for loop time cost is: {}s'.format(res_0, time_1 - time_0))
print ('The result is: {}, and concurrent time cost is : {}s'.format(res_1, time_3 - time_2))
如果有感兴趣的童鞋也可以去尝试一下,在这种场景下的并行运算,如果参量y不是一个可迭代式的变量,是无法用zip压缩传到map函数中去的。上述代码的运行结果如下:
The result is: [3. 6. 9.], and for loop time cost is: 6.005392789840698s
The result is: [3. 6. 9.], and concurrent time cost is : 2.0451698303222656s
这个计算时长其实就约等于休眠时长,因为这里我们开启了3个进程来进行休眠,因此并行时长是2s。
Jax自动微分场景
这里我们用Jax的自动微分框架做一个示例,没有安装Jax和Jaxlib的想运行需要自行安装相关软件。虽然在Jax的grad函数中,支持argnums这样的参数配置,但从代码层面角度来说,总是显得可读性并不好。正常情况下我们算偏导数\(\frac{\partial f(x,y)}{\partial x}\)其实更合理的表述应该是\(\frac{\partial P(x)}{\partial x}\)。而如果按照Jax这种写法,更像是从\([\frac{\partial f(x, y)}{\partial x}, \frac{\partial f(x, y)}{\partial x}]\)两个元素中取了第一个元素。当然,这只是表述上的问题,也是我个人的理解,其实并不影响程序的正确性。这里使用partial偏函数的相关案例如下所示:
from functools import partial
from jax import grad
from jax import numpy as jnp
# Jax要求grad函数输出结果为标量,所以要加一项求和
def mul(x, y):
f = x * y
return f.sum()
# 定义输入变量
x = jnp.array([1, 2, 3], jnp.float32)
y = 3.
# 定义偏函数和对应偏导数
partial_mul = partial(mul, y=y)
grad_mul = grad(partial_mul)
print (grad_mul(x))
执行结果如下:
[3. 3. 3.]
总结概要
本文介绍了在Python中使用偏函数partial的方法,并且介绍了两个使用partial函数的案例,分别是concurrent并行场景和基于jax的自动微分场景。在这些相关的场景下,我们用partial函数更多时候可以使得代码的可读性更好,在性能上其实并没有什么提升。如果不想使用partial函数,类似的功能也可以使用参考链接中所介绍的方法,实现一个装饰器,也可以做到一样的功能。
版权声明
本文首发链接为:https://www.cnblogs.com/dechinphy/p/partial.html
作者ID:DechinPhy
更多原著文章:https://www.cnblogs.com/dechinphy/
请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html
参考链接
Python代码中的偏函数的更多相关文章
- Kivy A to Z -- 怎样从python代码中直接訪问Android的Service
在Kivy中,通过pyjnius扩展能够间接调用Java代码,而pyjnius利用的是Java的反射机制.可是在Python对象和Java对象中转来转去总让人感觉到十分别扭.好在android提供了b ...
- pycharm运行Pytest,有没有将Pytest写入Python代码中的区别
初学pytest. 将pytest写进Python代码中 不同运行方式都可正常运行 =======================**********************========= ...
- python代码中判断版本
在python代码中判断python版本: if sys.version_info < (3, 0): lib.make_flows.argtypes = [c_char_p, c_char_p ...
- 如何在创建hive表格的python代码中导入外部文件
业务场景大概是这样的,我要对用户博文进行分词(这个步骤可以看这篇文章如何在hive调用python的时候使用第三方不存在的库-how to use external python library in ...
- python代码中pass的用法
我们有时会在方法中写一些注释代码,用来提示这个方法是干嘛的之类,看下面代码: class Game_object: def __init__(self, name): self.name = name ...
- 解决python代码中含有中文报错
python中写入中文时报错如下图所示: 依照网上解决方法:在py文件中加入:#encoding=utf-8 然后继续报错如下图所示: 解决方法: 在py文件中加入: import sysreload ...
- Python代码中func(*args, **kwargs)
这是Python函数可变参数 args及kwargs *args表示任何多个无名参数,它是一个tuple **kwargs表示关键字参数,它是一个dict 测试代码如下: def foo(*args, ...
- python代码中碰到的问题及解决
一.针对raw_input输入的字符进行类型判断及转换: raw_input输入默认为字符,如果输入的是数字字符,想自动转换,即:输入为a,不做操作,如果输入为3,即转化为整数. 可利用try..ex ...
- python代码中使用settings
在具体的Django应用中,通过引入 django.conf.settings 使用配置,例: from django.conf import settings settings.configure( ...
- 生成.m文件的python代码中出现的错误
错误代码 import tempfile import subprocess import shlex import os import numpy as np import scipy.io scr ...
随机推荐
- Ubuntu SVN服务端安装方法
Ubuntu SVN服务端安装方法:https://blog.csdn.net/sm_wang/article/details/78656120https://www.cnblogs.com/myme ...
- windows无法连接VMware虚拟机的linux
遇到的问题:今天使用xshell连接虚拟机,无法连接. 解决过程: 1.测试ping, linux虚拟机能ping通windows主机,可是windows主机ping不通linux虚拟机. 2.查看v ...
- PYQT5学习(13):QMidArea同时显示多个窗口,创建多个独立的窗口
QMidArea 参考文章:https://blog.csdn.net/jia666666/article/details/81670569 一种同时显示多个窗口的方法,创建多个独立的窗口,这些独立 ...
- (数据科学学习手札154)geopandas 0.14版本新特性一览
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 大家好我是费老师,就在前两天,Python生态中 ...
- Django框架——Django与Ajax、分页器
文章目录 1 Django与Ajax 一 什么是Ajax 优点: 二 基于jquery的Ajax实现 Ajax-->服务器-->Ajax执行流程图 三 案例 一 通过Ajax,实现前端输入 ...
- Skywalking APM监控系列(二、Mysql、Linux服务器与前端JS接入Skywalking监听)
前言 上篇我们介绍了Skywalking的基本概念与如何接入.Net Core项目,感兴趣可以去看看: Skywalking APM监控系列(一丶.NET5.0+接入Skywalking监听) 本篇我 ...
- 回文自动机(PAM) 详解
PAM 是一种高效存储字符串中所有回文子串的自动机,用于解决回文串相关问题. 虽然代码稍微长一点,但写起来比 manacher 容易很多,毕竟没有加了一堆字符再转回原串的若干上取整下取整问题. 前置知 ...
- 让物体动起来,Unity的几种移动方式
一.前言 在大部分的Unity游戏开发中,移动是极其重要的一部分,移动的手感决定着游戏的成败,一个优秀的移动手感无疑可以给游戏带来非常舒服的体验.而Unity中有多种移动方法,使用Transform, ...
- 2D物理引擎 Box2D for javascript Games 第六章 关节和马达
2D物理引擎 Box2D for javascript Games 第六章 关节和马达 关节和马达 到现在你所见到的所有类型的刚体有着一些共同点:它们都是自由的并且在除碰撞的请款之外,彼此没有依赖. ...
- 【Vue3响应式原理#02】Proxy and Reflect
专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核推荐 欢迎各位ITer关注点赞收藏 背景 以下是柏成根据Vue3官方课程整理的响应式书面文档 - 第二节, ...