关于python:为什么sympy lamdify函数不能识别numpy sum函数和multiply函数

Why sympy lambdify function cannot identify numpy sum function and multiply function

我想使用 sympy 和 numpy 来学习机器学习。因为 symoy 提供了非常方便的偏导数计算。
但是在使用过程中发现sympy的lambdify函数并不能识别numpy的sum函数和multiply函数。

以下面的例子为例

1
2
3
4
5
6
7
8
y_ = np.sum(np.dot(w,x)+b)
print(y_)
y_f = lambdify((w,x,b),y_,"numpy")
w_l = np.mat([1,1,1,1,1])
x_l= np.mat([1,1,1,1,1]).T
b_l = np.mat([0,0,0,0,0]).T
y_l = np.mat([6,6,6,6,6]).T
print(y_f(w_l,x_l,b_l))
1
2
3
4
5
6
7
8
b + w*x
[[5]
 [5]
 [5]
 [5]
 [5]]

Process finished with exit code 0
1
2
3
4
5
6
7
8
y_ = np.multiply(w,x)+b
print(y_)
y_f = lambdify((w,x,b),y_,"numpy")
w_l = np.mat([1,1,1,1,1]).T
x_l= np.mat([1,1,1,1,1]).T
b_l = np.mat([0,0,0,0,0]).T
y_l = np.mat([6,6,6,6,6]).T
print(y_f(w_l,x_l,b_l))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
b + w*x
Traceback (most recent call last):
  File"G:/lijie/PycharmProjects/hw3/test.py", line 24, in <module>
    print(y_f(w_l,x_l,b_l))
  File"<lambdifygenerated-1>", line 2, in _lambdifygenerated
  File"C:\\Users\\lijie\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages\
umpy\\matrixlib\\defmatrix.py"
, line 220, in __mul__
    return N.dot(self, asmatrix(other))
ValueError: shapes (5,1) and (5,1) not aligned: 1 (dim 1) != 5 (dim 0)
b + w*x
Traceback (most recent call last):
  File"G:/lijie/PycharmProjects/hw3/test.py", line 24, in <module>
    print(y_f(w_l,x_l,b_l))
  File"<lambdifygenerated-1>", line 2, in _lambdifygenerated
  File"C:\\Users\\lijie\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages\
umpy\\matrixlib\\defmatrix.py"
, line 220, in __mul__
    return N.dot(self, asmatrix(other))
ValueError: shapes (5,1) and (5,1) not aligned: 1 (dim 1) != 5 (dim 0)

如您所见,lambdify 仅接受 lamda 表达式而不检查操作符号。如何解决这个问题呢。谢谢你的帮助


混合 numpysympy 可能很棘手;再加上由 np.mat 而不是基本数组类型 ndarray.

引起的潜在混淆

总共

1
y_ = np.sum(np.dot(w,x)+b)

在 sympy 对象上评估 python/numpy 表达式。结果是一个 sympy 表达式 w*x+b。 sympy 对象是标量,因此这不会对任何类型的矩阵乘法或数组求和进行编码。 multiply 表达式的计算方式相同。

lambdify 表达式然后将相同的 y_ 转换为相同的 Python 函数。该评估取决于 np.mat 参数的维度和类。

细节

暂时忽略 sympy 部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
In [310]: w = np.mat([1,1,1,1,1])
     ...: x= np.mat([1,1,1,1,1]).T
     ...: b = np.mat([0,0,0,0,0]).T
     ...: y = np.mat([6,6,6,6,6]).T                                            
In [311]: np.sum(np.dot(w,x)+b)                                                
Out[311]: 25
In [312]: np.multiply(w,x)+b                                                    
Out[312]:
matrix([[1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1]])

因为它们是 np.mat,所以 wx 都是 2d:

1
2
3
4
In [316]: w.shape                                                              
Out[316]: (1, 5)
In [317]: x.shape                                                              
Out[317]: (5, 1)

np.dot of (1,5) with (5,1) 是 (1,1) 结果:

1
2
In [313]: np.dot(w,x)                                                          
Out[313]: matrix([[5]])

,对于 np.matrix* 被定义为 dot:

1
2
In [314]: w*x                                                                  
Out[314]: matrix([[5]])

元素方面:

1
2
3
4
5
6
7
In [315]: np.multiply(w,x)         # elementwise produces (5,5)                                  
Out[315]:
matrix([[1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1]])

np.sum(np.dot(w,x)+b) 执行 dot,然后添加 b,并以 sum 结束所有元素。

np.multiply(w,x)+b 做这个乘法,加上 b。没有 sum.

更正

使用我第一次错过的w.T

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [322]: np.multiply(w.T,x)                                                    
Out[322]:
matrix([[1],
        [1],
        [1],
        [1],
        [1]])
In [323]: w.T*x                                                                
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-323-11ad839cfa88> in <module>
----> 1 w.T*x

/usr/local/lib/python3.6/dist-packages/numpy/matrixlib/defmatrix.py in __mul__(self, other)
    218         if isinstance(other, (N.ndarray, list, tuple)) :
    219             # This promotes 1-D vectors to row vectors
--> 220             return N.dot(self, asmatrix(other))
    221         if isscalar(other) or not hasattr(other, '__rmul__') :
    222             return N.dot(self, other)

<__array_function__ internals> in dot(*args, **kwargs)

ValueError: shapes (5,1) and (5,1) not aligned: 1 (dim 1) != 5 (dim 0)

(5,1) 和 (5,1) 的

np.multiply 产生 (5,1),逐元素乘法

w.T*xnp.mat 的矩阵乘法,因此 np.dot 错误。

不鼓励使用 np.mat(如果没有正式弃用)。在 numpy 中,添加 matmul/@ 消除了它的符号优势。如果您坚持使用基本数组类 ndarraynumpy 中的生活会更简单。我意识到 sympy 仍然使用二维矩阵概念,* 作为矩阵乘法。

同情

isympy 会话中,我发现我需要将 w,x,b 定义为符号:

1
y_ = np.sum(np.dot(w,x)+b)

如果 w,x,b 只是符号,则它们是标量,而不是矩阵或数组。您的 np.sum(np.dot(1,2)+4)np.multiply(1,2)+41*2+4 都产生相同的结果。只有当变量是数组、np.matsympy.Matrix 时,表达式才不同。

问题不在于 lambdify。在这两种情况下,它都被赋予相同的 y_(由 print(y_) 验证。你会得到错误,因为参数是 np.mat,而 * 是矩阵乘法。

使用 x,y,z 符号:

1
In [55]: f = lambdify((x,y,z),x*y+z, 'numpy')

使用isympy自省:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [56]: f??                                                                    
Signature: f(x, y, z)
Docstring:
Created with lambdify. Signature:

func(x, y, z)

Expression:

x*y + z

Source code:

def _lambdifygenerated(x, y, z):
    return (x*y + z)


Imported modules:
Source:  
def _lambdifygenerated(x, y, z):
    return (x*y + z)
File:      ~/mypy/<lambdifygenerated-4>
Type:      function

阅读 lambdify 的完整文档。请注意,它基本上是一个词法替换

https://docs.sympy.org/latest/modules/utilities/lambdify.html

本文档警告:

As a general rule, NumPy
functions do not know how to operate on SymPy expressions, and SymPy
functions do not know how to operate on NumPy arrays. This is why lambdify
exists: to provide a bridge between SymPy and NumPy.

象征

https://docs.sympy.org/latest/modules/core.html#module-sympy.core.sympify

说它使用 eval。将 x,y,z 定义为符号:

1
2
3
4
5
6
7
8
In [66]: eval('np.dot(x,y)+z')                                                  
Out[66]: x?y + z

In [67]: eval('np.sum(np.dot(x,y)+z)')                                          
Out[67]: x?y + z

In [68]: eval('np.multiply(x,y)+z')                                            
Out[68]: x?y + z

换句话说,它只是将符号传递给 numpy 函数(和/或运算符),

1
2
In [69]: np.dot(x,y)                                                            
Out[69]: x?y

dot 将其输入转换为数组:

1
2
3
4
5
In [70]: np.array(x)                                                            
Out[70]: array(x, dtype=object)

In [71]: np.dot(np.array(x), np.array(y))                                      
Out[71]: x?y

这是有效的,因为符号定义了 \\'*\\' 和 \\' \\'。

sympy 文档警告说,评估 numpy 并不"知道"有关 sympy 对象的任何信息。它将它们视为对象 dtype 数组,这可能有效也可能无效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [72]: sin(x)       # sympy sin                                                          
Out[72]: sin(x)

In [73]: np.sin(x)        # numpy sin                                                      
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
AttributeError: 'Symbol' object has no attribute 'sin'

The above exception was the direct cause of the following exception:

TypeError                                 Traceback (most recent call last)
<ipython-input-73-92f2c2d0df9d> in <module>
----> 1 np.sin(x)

TypeError: loop of ufunc does not support argument 0 of type Symbol which has no callable sin method

np.sin 执行 np.sin(np.array(x)),然后将操作委托给 xsin 方法 - 该方法不存在。