编写神经网络代码时出现不一致的Python错误求助
这是之前提问的后续,我在编写并运行神经网络代码时持续遇到非一致性错误:同一代码运行时会出现不同报错。部分原因可归为随机函数导致的数据变化,但并非全部——用np.save保存数据后关闭Python,再重新用np.load加载,错误可能消失。
可复现代码
import numpy as np import random def generate_data(n,var0,var1): data=[] for i in range(n): z=np.random.binomial(1,0.5) if z==0: x,y=np.random.multivariate_normal(mean=[0,0],cov=[[1,var0],[var0,1]]) elif z==1: x,y=np.random.multivariate_normal(mean=[1,1],cov=[[1,var1],[var1,1]]) data.append([x,y,z]) return data def cross_validation(data): n=len(data) copy=list(data) random.shuffle(copy) part0=copy[0:int(n/5)] part1=copy[int(n/5):int(2*n/5)] part2=copy[int(2*n/5):int(3*n/5)] part3=copy[int(3*n/5):int(4*n/5)] part4=copy[int(4*n/5):n] partitioned_copy=[part0,part1,part2,part3,part4] folds=[] for i in range(5): training_set=[] for j in range(5): if j!=i: for point in partitioned_copy[j]: training_set.append(point) testing_set=partitioned_copy[i] folds.append([training_set,testing_set]) return folds # 对数域下的加减运算函数 def logadd(x): n=np.max([int(num) for num in x]) y=[num-n for num in x] ey=np.exp(y) return n+np.log(np.sum(ey)) def logsubtract(a,b): n=np.max([int(a),int(b)]) return n+np.log(np.exp(a-n)-np.exp(b-n)) def initialize_network(layer_sizes): W=[] b=[] for i in range(len(layer_sizes)): if i==0: W.append(np.random.randn(layer_sizes[i],2)) else: W.append(np.random.randn(layer_sizes[i],layer_sizes[i-1])) b.append(np.zeros(layer_sizes[i])) return W,b def neural_network_forward(X,Y,W,b): depth=len(W) z=[] a=[] for i in range(depth): weights=np.array(W[i]) biases=np.array([[b[i][j] for k in range(len(X))] for j in range(len(b[i]))]) if i==0: inputs=np.array([X,Y]) z.append(np.matmul(weights,inputs)+biases) else: inputs=a[len(a)-1] z.append(np.matmul(weights,inputs)+biases) if i<depth-1: a.append([[np.amax(z[len(z)-1][j][k],0) for k in range(len(X))] for j in range(np.shape(z[len(z)-1])[0])]) else: z_final=[[z[len(z)-1][j][k] for k in range(len(X))] for j in range(2)] a.append([np.exp(z_final[1][k])/(np.exp(z_final[0][k])+np.exp(z_final[1][k])) for k in range(len(X))]) loga_final=[z_final[1][k]-logadd([z_final[0][k],z_final[1][k]]) for k in range(len(X))] return z,a,loga_final def neural_network_backward(X,Y,Z,W,b,z,a): depth=len(W) da=[0 for i in range(len(a))] dz=[0 for i in range(len(z))] dW=[0 for i in range(len(W))] db=[0 for i in range(len(b))] da[len(da)-1]=[-Z[k]/(a[len(a)-1][k]*len(Z))+(1-Z[k])/((1-a[len(a)-1][k])*len(Z)) for k in range(len(Z))] dz[len(dz)-1]=[[(Z[k]-a[len(a)-1][k])/len(Z) for k in range(len(Z))],[(a[len(a)-1][k]-Z[k])/len(Z) for k in range(len(Z))]] dW[len(dW)-1]=np.matmul(dz[len(dz)-1],np.transpose(a[len(a)-2])) db[len(db)-1]=[[np.sum([dz[len(dz)-1][i][j] for j in range(np.shape(dz[len(dz)-1])[1])])] for i in range(np.shape(dz[len(dz)-1])[0])] for i in reversed(range(depth-1)): da[i]=np.matmul(np.transpose(W[i+1]),dz[i+1]) dz[i]=np.empty([np.shape(z[i])[0],len(X)]) for j in range(np.shape(z[i])[0]): for k in range(len(X)): if z[i][j][k]>0: dz[i][j][k]=da[i][j][k] elif z[i][j][k]<0: dz[i][j][k]=0 if i>0: dW[i]=np.matmul(dz[i],np.transpose(a[i-1])) elif i==0: dW[i]=np.matmul(dz[i],np.transpose(np.array([X,Y]))) db[i]=[[np.sum([dz[i][j][k] for k in range(np.shape(dz[i])[1])])] for j in range(np.shape(dz[i])[0])] return da,dz,dW,db def cost(X,Y,Z,W,b,loga_final): total_loss=0 def loss(loga,z): return -(z*loga+(1-z)*logsubtract(0,loga)) for k in range(len(Z)): total_loss+=loss(loga_final[k],Z[k])/len(Z) return total_loss def gradient_descent(X,Y,Z,W_start,b_start,learning_rate): cost_history=[] forward=neural_network_forward(X,Y,W_start,b_start) previous_cost=np.inf current_cost=cost(X,Y,Z,W_start,b_start,forward[2]) cost_history.append(current_cost) a=forward[1] z=forward[0] W=W_start b=b_start while previous_cost-current_cost>=0.001: backward=neural_network_backward(X,Y,Z,W_start,b_start,forward[0],forward[1]) backward02=learning_rate*backward[0][2] break return backward02 # 原函数还有更多逻辑,这里只保留会出错的部分并直接break def neural_network(folds): basic_accuracies = [] correct_predictions = 0 # 临时定义避免未定义错误 for i in range(1): # 测试用只跑1折,实际应该是range(5) training_set=folds[i][0] testing_set=folds[i][1] X=[training_set[k][0] for k in range(len(training_set))] Y=[training_set[k][1] for k in range(len(training_set))] Z=[training_set[k][2] for k in range(len(training_set))] basic_accuracies.append(correct_predictions/len(testing_set)) W,b=initialize_network([3,5,2]) return X,Y,Z,W,b # 原函数还有更多逻辑,这里只保留到能获取参数变量的部分 grid_values=[0.1*i for i in range(-9,-8)] # 测试用只取一个值 progress=0 for var0 in grid_values: for var1 in grid_values: print(progress) data=generate_data(50000,var0,var1) X=[data[i][0] for i in range(len(data))] Y=[data[i][1] for i in range(len(data))] Z=[data[i][2] for i in range(len(data))] folds=cross_validation(data) X,Y,Z,W,b=neural_network(folds) backward02=gradient_descent(X,Y,Z,W,b,0.01) progress+=1
出现的两种错误
- UnboundLocalError: local variable 'backward02' referenced before assignment(指向
return backward02行) - TypeError: can't multiply sequence by non-int of type 'float'(指向
backward02=learning_rate*backward[0][2]行)
我无法理解第一个错误:while循环理应执行一次后break,会在return前给backward02赋值。对于第二个错误,删除乘以learning_rate的操作后错误消失;查看变量发现backward[0][2]是浮点列表,但直接乘浮点数会报错,然而用np.save保存后关闭Python再重新加载,就能正常乘0.01。
1. UnboundLocalError的原因
当previous_cost - current_cost < 0.001时,while循环条件不满足,循环体完全不会执行,此时backward02从未被赋值,直接return就会触发这个错误。
触发这个情况的核心原因是:初始损失计算可能出现异常值。比如loga_final中如果出现nan或inf,会导致current_cost变成nan,此时np.inf - nan的结果还是nan,而nan >= 0.001的判断结果为False,循环直接跳过。
2. TypeError的原因
backward[0][2]是Python原生列表,Python规则里列表只能和整数相乘(实现列表重复的效果),和浮点数相乘就会触发类型错误。
保存再加载后能正常运行的原因:np.save会自动将列表转为numpy数组,加载后得到的是numpy数组,而numpy数组支持和浮点数做元素级乘法,因此不会报错。
额外潜在问题
代码里还有几处未定义变量的错误,比如原neural_network函数里的basic_accuracies和correct_predictions未提前定义,这也会导致运行时错误,建议优先修复这类基础问题。
内容的提问来源于stack exchange,提问作者J.D.

