Python实现最优步长梯度下降时,循环中numpy.norm报错咨询
带最优步长的梯度下降实现报错解决
问题描述
我在Python中实现带最优步长的梯度下降时遇到如下错误:
AttributeError: 'Float' object has no attribute 'sqrt'
if (np.linalg.norm(np.array(dk)) < eps): break
File <array_function internals>:200 in norm
File C:\ProgramData\anaconda3\Lib\site-packages\numpy\linalg\linalg.py:2512 in norm
ret = sqrt(sqnorm)TypeError: loop of ufunc does not support argument 0 of type Float which has no callable sqrt method
我的实现代码如下:
import sympy as sp import numpy as np def grad(f): X = f.free_symbols Y = [f.diff(xi) for xi in X] return [x_k for x_k in X], Y def descente_pas_opti(f, X0, eps = 1e-6): Xk = X0 fonction = sp.sympify(f) X, gradform = grad(fonction) r=sp.symbols('r') dform= np.array([-df_k for df_k in gradform]) while True: dk=np.array( [df_k.subs( [(X[k],Xk[k]) for k in range(len(X))]) for df_k in dform] ) rho = sp.solve( np.dot( [df_k.subs( [(X[k], Xk[k] + r*dk[k]) for k in range (len(X))] ) for df_k in gradform] , dk) , r)[0] Xk = [Xk[0]+rho*dk[0], Xk[1]+rho*dk[1]] if (np.linalg.norm(dk) < eps): break return Xk
调用参数:
descente_pas_opti('5*x**2 + 0.5*y**2 -3*(x + y)', [-2,-7])
补充:循环外单独测试相关代码正常,首次迭代时np.linalg.norm(dk)返回约25.07,但放入循环内就报错。
问题原因
报错核心是dk数组里的元素不是numpy浮点类型,而是sympy的Float对象。numpy的linalg.norm无法直接处理sympy的数值类型;第二次循环时,Xk里的元素因rho是sympy解出的符号值,与dk的sympy Float相乘后仍为sympy类型,导致后续计算dk时,subs得到的还是sympy Float,传给numpy norm就触发报错。
解决代码及说明
修改后的代码:
import sympy as sp import numpy as np def grad(f): X = f.free_symbols Y = [f.diff(xi) for xi in X] return [x_k for x_k in X], Y def descente_pas_opti(f, X0, eps = 1e-6): Xk = X0 fonction = sp.sympify(f) X, gradform = grad(fonction) r = sp.symbols('r') dform = np.array([-df_k for df_k in gradform]) while True: # 计算dk并转换为numpy浮点数组 dk = np.array( [float(df_k.subs([(X[k], Xk[k]) for k in range(len(X))])) for df_k in dform] ) # 计算梯度点积并求解最优步长,转换为浮点值 grad_at_xkr = [df_k.subs([(X[k], Xk[k] + r * dk[k]) for k in range(len(X))]) for df_k in gradform] dot_product = sum(g * dk[i] for i, g in enumerate(grad_at_xkr)) rho = float(sp.solve(dot_product, r)[0]) # 更新Xk为普通数值 Xk = [Xk[i] + rho * dk[i] for i in range(len(X))] if np.linalg.norm(dk) < eps: break return Xk
关键修改点:
- 计算
dk时,用float()将sympy数值转为普通浮点,确保dk是numpy浮点数组; - 求解
rho后立即转为float,避免后续计算引入sympy类型; - 用Python原生
sum替代np.dot计算点积,避免sympy表达式与numpy数组混合运算的问题; - 确保
Xk始终存储普通数值,保证后续subs结果能顺利转成浮点。
内容的提问来源于stack exchange,提问作者douze55
相关产品推荐
相关产品推荐

