首页 > 编程语言 >Python代码中的偏函数

Python代码中的偏函数

时间:2023-12-21 17:56:15浏览次数:43  
标签:partial 函数 Python res 代码 result time mul

技术背景

在数学中我们都学过偏导数\(\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

我们现在来分析一下上面这个案例中所体现的信息:

  1. 在使用partial函数时使用的是关键字参数,即时原本的变量不是一个关键字参数,而是一个位置参数。
  2. 虽然得到的偏函数partial_mul运行的方式跟函数一致,但其实它是一个partial的对象类型。
  3. 在生成partial_mul对象时已经执行过一遍函数,因此函数中的打印语句被打印了两次。
  4. 偏函数的计算结果肯定是跟原函数保持一致的,但是在一些特殊场景下,我们可能会用到这种单变量的偏函数。

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

参考链接

  1. https://www.cnblogs.com/huyangblog/p/8999866.html

标签:partial,函数,Python,res,代码,result,time,mul
From: https://www.cnblogs.com/dechinphy/p/partial.html

相关文章

  • 解决Python中main运行报错的方法
    在Python开发中,main函数是程序的入口,是程序执行的起点。然而,有时候我们在运行main函数时可能会遇到各种报错。本文将介绍一些常见的Pythonmain运行报错,并提供相应的解决办法。1.ModuleNotFoundError:这个错误表示Python无法找到指定的模块。解决方法如下:-确保模块已经正确安装。可......
  • 无涯教程-Go - 函数指针
    Go编程语言使您可以将指针传递给函数,只需将函数参数声明为指针类型。在下面的示例中,我们将两个指针传递给一个函数,并更改该函数内部的值,该值会反映在调用函数中-packagemainimport"fmt"funcmain(){/*局部变量定义*/varaint=100varbint=200fmt.P......
  • 为什么在Python类中经常会使用init函数
     在Python中,类是一种用于创建对象的蓝图或模板。当我们定义一个类时,经常会在类中定义一个名为`__init__`的函数,也称为构造函数或初始化方法。本文将解释为什么在Python类中经常会使用`__init__`函数,并介绍它的作用和用法。 1.初始化对象: `__init__`函数在创建类的对象时自动调......
  • 在 MySQL 中,你可以使用 `AVG()` 函数来计算一组值或表达式的平均值。`AVG()` 函数的基
    在MySQL中,AVG()函数在计算平均值时会自动忽略NULL值¹⁴。也就是说,它只会计算所有非空值的平均值³。例如,假设你有一个包含以下值的列:90,80,70,85,95,NULL,NULL。在这种情况下,AVG()函数将只计算非空值的平均值,即:而不是将NULL值视为0并计算所有值的平均值。如果你需......
  • day 03-2 Python基础-字符串格式化
    2.字符串格式化字符串格式化,使用跟便捷的形式实现字符串的拼接。%format(推荐)f2.1%2.1.1基本格式化操作#%s是占位符,也成为字符串占位符#后面空格加%text="我叫%s,今年18岁"%"linzai"#:%前面加上一个空格print(text)name="linzai"text="我叫%s,今年18岁"......
  • [转]SAP PS常用事务代码T-CODE
    SAPPS常用事务代码:工作分解结构CJ01创建WBSCJ02更改WBSCJ03显示WBS日期CJ21更改基本日期CJ22显示基本日期CJ23更改预测日期CJ24显示预测日期CJ25更改实际日期CJ26显示实际日期网络CN21创建网络CN22更改网络CN23显示网络结构计划CJ20更改项目项目构造器CJ20N项目构造器项目负责......
  • 医保购药小程序与智能医疗的代码融合
    在当今数字时代,医保购药小程序的兴起为智能医疗开创了崭新的篇章。通过技术代码的巧妙运用,这一小程序不仅为患者提供了便捷的购药体验,同时在医保结算、用药监控等方面实现了高度智能化,让我们一起深入代码世界,探索其背后的技术奥秘。#医保购药小程序的用户类定义classUser:d......
  • 第12次上机内容 函数
    1、阅读程序(1)作用:打印a的值。分析结果:38。运行结果:考察函数传值,不是经典转换ab值不是很认可。(2)说什么来什么,不过很没必要省着点空间吧?作用:打印x、y的值或者应该是交换x、y的值未果,证明函数传值不修改变量值。分析结果3,55,3运行结果......
  • 无论怎么写代码总会遇到下面额问题
    Noqualifyingbeanoftype'org.springframework.transaction.TransactionManager'available:expectedsinglematchingbeanbutfound2:dataSourceTransactionManager,kafkaTransactionManagerNoqualifyingbeanoftype'TransactionManager'......
  • Python 把包含\\u4f20\\u5a92 unicode内容的字典字符串变成字典
    importjson#把包含\\u4f20\\u5a92unicode内容的字典字符串变成字典deftext_to_dict(text):dict1=json.loads(text)str_dict=str(dict1).replace('\\xa0','').replace('\'','"')dict_json=json.loads(s......