【numpy手搓DNN极简版】 numpy手搓DNN极简版简介公式使用示例完整代码简介开始直接按照begin函数生成权重结构例begin(8,4,2)为输入层有8个输入隐藏层4输出输出层2输出。隐藏层默认用leaky_relu激活函数输出层默认用sigmoid激活函数公式前向传播可以用yaxb函数代替只不过函数中用的是矩阵运算。然后用激活函数zsigmoid(y)激活输出就可以了。反向传播用dY/dadY/dzdz/dydy/da方差(Y,z)*d_sigmoid(y)x 得到结果后用aa-dY/da就是后面用的 W[layer_idx] -learnVdL_dw 其中learnV是学习率。用递归函数计算多隐藏层的前向反向传播。使用示例if__name____main__:# 准备数据两个样本输入8维输出2维X[[0.1,0.2,0.3,0.1,0.2,0.3,0.9,0.6],[0.3,0.5,0.8,0.3,0.5,0.8,0.7,0.3]]Y[[0.5,0.8],[0.6,0.7]]# load(my_model.npz) 可以直接加载模型# 训练阶段注释掉后可直接加载模型测试begin(8,8,4,2)#可以设为多隐藏层# begin(8, 4, 2) 也可以设为单隐藏层randomWB()# 随机权重train()#train(epochs10000)不设参数默认跑10000次test()#用X Y测试可以在此之前重设X Ysave(my_model.npz)#可以保存训练好的模型完整代码importnumpyasnp X[]Y[]xNoneyNonelayers[]# 每层神经元个数W[]# 权重列表b[]# 偏置列表learnV0.1z_list[]# 每层的 za_list[]# 每层的 adefsigmoid(x):return1/(1np.exp(-x))defd_sigmoid(x):osigmoid(x)returno*(1-o)defrelu(x):returnnp.maximum(0,x)defd_relu(x):return(x0).astype(float)defleaky_relu(x,alpha0.01):returnnp.where(x0,x,alpha*x)defd_leaky_relu(x,alpha0.01):returnnp.where(x0,1.0,alpha)defloss(L,y):returnnp.sum((L-y)**2)/2defforward_recursive(layer_idx,inp):递归前向传播返回最后一层输出iflayer_idxlen(W):returninp zinp W[layer_idx]b[layer_idx]globallayers# 最后一层用sigmoid,其他层用leaky_reluiflayer_idx1len(W):asigmoid(z)else:aleaky_relu(z)z_list.append(z)a_list.append(a)returnforward_recursive(layer_idx1,a)defbackward_recursive(layer_idx,grad_output,network_input):递归反向传播iflayer_idx0:current_inputnetwork_input# (batch, in_units)else:current_inputa_list[layer_idx-1]# (batch, in_units)zz_list[layer_idx]# (batch, out_units)globallayers# 最后一层用sigmoid,其他层用leaky_reluiflayer_idx2layers.__len__():da_dzd_sigmoid(z)else:da_dzd_leaky_relu(z)dL_dzgrad_output*da_dz dL_dwcurrent_input.T dL_dz dL_dbnp.sum(dL_dz,axis0,keepdimsTrue)grad_inputdL_dz W[layer_idx].T# 防止梯度爆炸dL_dwnp.clip(dL_dw,-1.0,1.0)dL_dbnp.clip(dL_db,-1.0,1.0)W[layer_idx]-learnV*dL_dw b[layer_idx]-learnV*dL_db.flatten()iflayer_idx0:backward_recursive(layer_idx-1,grad_input,network_input)defbegin(*args):定义网络结构begin(输入维度, 隐藏层1, ..., 输出维度)globallayers,W,b,x,y,isLoad isLoadFalselayerslist(args)iflen(layers)2:raiseValueError(至少需要输入维度和输出维度)W.clear()b.clear()foriinrange(len(layers)-1):in_dim,out_dimlayers[i],layers[i1]W.append(np.random.normal(0,0.1,size(in_dim,out_dim)))b.append(np.random.normal(0,0.1,sizeout_dim))# 如果已有数据则重新格式化ifXandY:xnp.array(X).reshape(-1,layers[0],1)ynp.array(Y).reshape(-1,layers[-1],1)else:xnp.array([])ynp.array([])defrandomWB():随机初始化权重和偏置保留当前结构foriinrange(len(layers)-1):in_dim,out_dimlayers[i],layers[i1]W[i]np.random.normal(0,0.1,size(in_dim,out_dim))b[i]np.random.normal(0,0.1,sizeout_dim)deftrain(epochs10000,early_stop1e-8,setLearnV0.7):globallearnV,z_list,a_list,isLoadforepochinrange(epochs):learnV1*(epochs-epoch)/epochs0.1ifisLoad:learnVsetLearnV total_loss0foriinrange(x.shape[0]):inpx[i]# (input_dim, 1)targety[i]# (output_dim, 1)z_list.clear()a_list.clear()outputforward_recursive(0,inp.T)# (1, output_dim)L_valloss(output,target.T)total_lossL_val dL_dyoutput-target.T backward_recursive(len(W)-1,dL_dy,inp.T)ifepoch%1000:print(fEpoch{epoch}, Loss:{total_loss/x.shape[0]:.6f})iftotal_loss/x.shape[0]early_stop:print(f提前停止于第{epoch}轮)breakdeftest():打印所有样本的网络输出k[]foriinrange(x.shape[0]):z_list.clear()a_list.clear()outforward_recursive(0,x[i].T)k.append(out)# print(np.around(out, decimals2))returnkdefsave(filename):保存模型包含网络结构和参数np.savez(filename,layersnp.array(layers),Wnp.array(W,dtypeobject),# 关键dtypeobject 允许不同形状bnp.array(b,dtypeobject))defload(filename):加载模型自动恢复结构 layers 和参数 W, bgloballayers,W,b,isLoad isLoadTruedatanp.load(filename,allow_pickleTrue)layersdata[layers].tolist()W[np.array(w,dtypefloat)forwindata[W]]# 从 object 数组转回列表b[np.array(bias,dtypefloat)forbiasindata[b]]# 确保每个元素是 numpy 数组加载后仍是数组print(f模型加载成功结构:{layers})defset(X[],Y[]):globalx,y,layers;xnp.array(X).reshape(-1,layers[0],1)ynp.array(Y).reshape(-1,layers[-1],1)# 使用示例 if__name____main__:# 准备数据两个样本输入8维输出2维X[[0.1,0.2,0.3,0.1,0.2,0.3,0.9,0.6],[0.3,0.5,0.8,0.3,0.5,0.8,0.7,0.3]]Y[[0.5,0.8],[0.6,0.7]]# load(my_model.npz)# 训练阶段注释掉后可直接加载模型测试begin(8,8,4,2)randomWB()train(epochs10000)test()save(my_model.npz)# 测试阶段直接加载模型