Skip to main content
 首页 » 编程设计

python之Numpy之函数在不应该更新全局变量时更新全局变量

2025年05月04日23bluestorm

我正在实现一种机器学习算法,该算法将矩阵近似为另外两个矩阵的倍数:V ~= WH。 W 和 H 随机初始化,并迭代更新,使 WH 更接近 V。

在我的代码中,在每次迭代中,我想 (i) 更新 W 和 H,以及 (ii) 根据 W 和 H 的新值计算分数。

我的问题是:我用来评分的函数应该只计算一个分数——它不应该影响 V、W 或 H——但它似乎会影响!我不知道为什么该函数会影响全局变量 - 我认为这只有在您声明 global foo 等形式时才会发生。结果是存在小差异 在计算的 W 和 H 中取决于每次迭代是否计算分数 - 这是没有意义的。

下面是我尽可能精简的一些代码 - 它没有实现我的算法或做任何有意义的事情,它只是重现了问题,即根据您是否评论计算出的 W 存在细微差异出计算分数的线。

谁能看出为什么这会改变结果?

import numpy as np 
 
# TRUE, GLOBAL VALUE OF V - should remain the same throughout 
V = np.array([[0.0, 4.0, 0.0, 4.0], 
              [0.0, 0.0, 1.0, 0.0], 
              [4.0, 0.0, 0.0, 3.0]]).astype(float) 
 
# RANDOM INITIALIZATIONS for two matrices, which are then updated by later steps 
W = np.array([[ 1.03796229,  1.29098839], 
              [ 0.49131664,  0.79759996], 
              [ 0.66055735,  0.48055734]]).astype(float) 
H = np.array([[ 0.06923306,  0.53105902,  1.1715193,   0.58126684], 
              [ 1.71226543,  0.54797385,  0.70978869,  1.58761463]]).astype(float) 
 
# A small number, which is added at some steps to prevent zero division errors/overflows 
min_no = np.finfo(np.float32).eps 
 
# A function which calculates SOME SCORE based on V_input - below is the simplest example that reproduces the error 
# This function should ONLY calculate and return a score - IT SHOULD NOT UPDATE GLOBAL VARIABLES! 
def score(V_input): 
 
    V_input[V_input == 0] = min_no # I believe that THIS LINE may be UPDATING GLOBAL V - but I don't understand why 
    scr = np.sum(V_input) 
 
    return scr 
 
# This function UPDATES the W matrix 
def W_update(Vw, Ww, Hw): 
 
    WHw = np.matmul(Ww, Hw) 
    WHw[WHw == 0] = min_no 
    ratio = np.matmul(np.divide(Vw, WHw), np.transpose(Hw)) 
 
    return np.multiply(Ww, ratio) 
 
# Repeated update steps 
for it in range(10): 
 
    # Update step 
    W = W_update(V, W, H) 
 
    # SCORING STEP - A SCORE IS CALCULATED - SHOULD NOT UPDATE GLOBAL VARIABLES 
    # HOWEVER, IT APPEARS TO DO SO - SMALL DIFFERENCES BETWEEN FINAL W WHEN COMMENTED OUT/NOT COMMENTED OUT 
    score_after_iteration = score(V) 
 
# THE OUTPUT PRINTED HERE IS DIFFERENT DEPENDING ON WHETHER OR NOT THE SCORING STEP IS COMMENTED OUT - WHY? 
print(W[:2,:2]) # Just a sample from W after last iteration 

请您参考如下方法:

如果您传递了一个变量,您传递了一个对该对象的引用。因此,如果您使用 V 调用函数,您会传递对矩阵 V 的引用,因此对矩阵的更新就是对该对象的编辑。例如,如果您传递对该列表的引用,然后该函数编辑该列表,那么您不是编辑该列表的副本,而是编辑列表本身,因此这些更改可以在外部看到电话。

但是您可以制作副本,例如:

for it in range(10): 
 
    # Update step 
    W = W_update(V, W, H) 
 
    score_after_iteration = score(<b>V.copy()</b>)

顺便说一句,W_update 也是如此,但这可能不是问题。