当前位置:首页 > 每日看点 > 正文内容

为什么我的神经网络输出永远一样?

卡卷网1年前 (2025-02-22)每日看点216

看了一下代码,确实有点东西嗷,自己写神经网络,自己写MSE损失函数,自己写梯度回传。(甚至连矩阵计算都不用,直接手写矩阵行元素的计算,好险不是矩阵元素两重循环计算。)

简单对代码梳理了一下,整个模型可以分为两个部分:

前向传播部分:

  • 压缩: 为什么我的神经网络输出永远一样?  第1张 ,具体为 为什么我的神经网络输出永远一样?  第1张
  • 解压: 为什么我的神经网络输出永远一样?  第1张 ,具体为 为什么我的神经网络输出永远一样?  第1张
  • 损失函数:为什么我的神经网络输出永远一样?  第1张

反向传播/.参数更新部分:

  • 梯度计算:为什么我的神经网络输出永远一样?  第1张 等计算,通过导数的链式法则等。
  • 参数更新: 为什么我的神经网络输出永远一样?  第7张 ,对相关参数进行更新。

输出的矩阵为什么是大都趋于0.5,我猜测,主要是因为数据生成采用的是np.random.random函数,这个函数会以均匀分布的方式生成(0,1)之间的随机数,所以矩阵中每一个元素的均值就是0.5,与此同时,在实现的代码中,是计算完所有的样本再进行梯度下降,很大概率是朝着0.5的方向下降。

那么这个问题就来了在修改好下降方向以后,为什么模型仍然不会学习得到输入 为什么我的神经网络输出永远一样?  第1张 是什么,输出 为什么我的神经网络输出永远一样?  第1张 就是什么的额能力呢?大概是当前模型的能力不足以学会更多的东西(参数较小)。


关于代码的一些吐槽

整个的计算效率很低,运行题主源代码都快运行了一个小时了。

可以加速计算的一个点是:当前选择的是对每一个数据进行计算

for i in range(len(data)): pass

这里可以使用batch加速计算一下,不然整个计算速度就太慢了。但是为了能够适配batch,又需要将相关的计算从for计算改成矩阵计算,比如

原始的前向传播,对每一行进行计算:

alpha=np.zeros(8) belta=np.zeros(8) delta=np.zeros(8) for i in range(8): alpha += x_input[i] * w_matrix_input[0, i] belta += x_input[i] * w_matrix_input[1, i] delta += x_input[i] * w_matrix_input[2, i] alpha += b_array_input[0] belta += b_array_input[1] delta += b_array_input[2] sigmoid_alpha = sigmoid(alpha) sigmoid_belta = sigmoid(belta) sigmoid_delta = sigmoid(delta) hidden_layer_output=np.array([sigmoid_alpha,sigmoid_belta,sigmoid_delta]) output = np.zeros((8,8)) line = np.zeros(8) for i in range(8): line=sigmoid_alpha*w_matrix_output[i,0]+sigmoid_belta*w_matrix_output[i,1]+sigmoid_delta*w_matrix_output[i, 2]+b_array_output[i] output[i]=line output=sigmoid(output) return output,hidden_layer_output

新的前向传播:

C = w_matrix_input@x_input + b_array_input.reshape(-1,1) hidden_layer_output = sigmoid(C) D = w_matrix_output@hidden_layer_output + b_array_output.reshape(-1,1) output=sigmoid(D) return output,hidden_layer_output

总之先把整个代码能够精简一点,可能才能看出更加多的问题吧

PS:可以先整个深度学习框架,不然这看着不太容易理解。


Numpy下的矩阵计算

由于原代码采用了大量地for循环和向量计算,这实际上造成了时间上的浪费,毕竟现代的计算方法矩阵计算已经很方便了,所以在这里,对原来的代码进行改进,使其能够利用矩阵的方式来计算。

对于前向传播来说,很简单,在上面也介绍了,可以直接在代码使用 A@B 做矩阵计算。那么接下来的任务就是将梯度求导这一块也改成矩阵计算,为了方便看客们的理解,在这里,做一个跟上述任务类似的求导过程计算就完事了:

首先,我们的预测是 为什么我的神经网络输出永远一样?  第1张 其中, 为什么我的神经网络输出永远一样?  第1张 就是我们的输出,假设维度是 为什么我的神经网络输出永远一样?  第1张 ,而 为什么我的神经网络输出永远一样?  第1张 就是我们的输入,维度是 为什么我的神经网络输出永远一样?  第1张为什么我的神经网络输出永远一样?  第1张 是我们的矩阵参数,维度是 为什么我的神经网络输出永远一样?  第1张为什么我的神经网络输出永远一样?  第1张 是我们的偏置,维度是 为什么我的神经网络输出永远一样?  第1张 ,相当于每一行的每一个元素都加上同一个偏置。

我们的损失函数有 为什么我的神经网络输出永远一样?  第1张 ,其实就是预测和真实值之间每一个元素做一个平方再求和,所以也可以写成 为什么我的神经网络输出永远一样?  第1张

那么,接下来,我们想知道,当前的损失函数数值,如何得到我们的参数 为什么我的神经网络输出永远一样?  第1张 的梯度呢?在这里就只讨论可微分的情况。因为直接计算矩阵的微分比较困难,但是我们可以先计算其中一个,比如 为什么我的神经网络输出永远一样?  第1张 ,即计算 为什么我的神经网络输出永远一样?  第1张 有:

【数学数字比较多,防止伤害眼睛,可跳过】

为什么我的神经网络输出永远一样?  第1张 其中为什么我的神经网络输出永远一样?  第1张 ,所以拆开来有:

为什么我的神经网络输出永远一样?  第1张

其中,当 为什么我的神经网络输出永远一样?  第1张 的时候,该导数为 为什么我的神经网络输出永远一样?  第1张 ,其余为0。

所以原式可转化为 为什么我的神经网络输出永远一样?  第1张 ,其中 为什么我的神经网络输出永远一样?  第1张为什么我的神经网络输出永远一样?  第1张 为1,其余为0。

所以就有 为什么我的神经网络输出永远一样?  第1张 ,最后矩阵形式为 为什么我的神经网络输出永远一样?  第1张

类似地,如果对参数 为什么我的神经网络输出永远一样?  第1张 ,也有上述的关系有 为什么我的神经网络输出永远一样?  第1张 ,实际上就是对列求和,所向量形式有 为什么我的神经网络输出永远一样?  第1张

【结束】

总之,通过上述的方式,我们能够将梯度计算通过矩阵的形式进行计算,对于原问题来说,我们就有:

为什么我的神经网络输出永远一样?  第37张代码如下:

# 矩阵梯度更新 def gradiengt_decent(output, input, w2, hidden_output): Derror_Dw2 = ((output - input) * output *(1-output)) @ hidden_output.T Derror_Db2 = np.sum((output - input) * output *(1-output), axis=1) Derror_Dw1 = ((w2.T @ (((output - input) * output *(1-output)))) * hidden_output * (1 - hidden_output)) @ input.T Derror_Db1 = np.sum(((w2.T @ (((output - input) * output *(1-output)))) * hidden_output * (1 - hidden_output)), axis=1) return Derror_Dw1,Derror_Dw2,Derror_Db1,Derror_Db2

PS:因为原代码没有用到W2,大概率是有问题的。

至此,基于numpy下的矩阵计算就修改完了。

深度学习框架

通过上述的矩阵导数计算可以知道,手动计算一个参数的梯度求导很简单,但是计算效率低;计算一个矩阵的梯度就有难度,但是计算效率高。如果能够有一个自动求导微分的工具,那该多好啊。这也是当前的深度学习发展很关键的一部分——自动微分机制

接下来,将采用当前主流的深度学习框架Pytorch来实现,同时验证文章上述梯度计算的正确性。

首先是定义网络以及前向传播:

import torch from torch import nn class SAE(nn.Module): def __init__(self) -> None: super().__init__() # 参数初始化 self.W1 = nn.parameter.Parameter(torch.zeros(3, 8, dtype=torch.double)) self.b1 = nn.parameter.Parameter(torch.zeros(3, dtype=torch.double)) self.sigmoid = nn.Sigmoid() self.W2 = nn.parameter.Parameter(torch.zeros(8, 3, dtype=torch.double)) self.b2 = nn.parameter.Parameter(torch.zeros(8, dtype=torch.double)) def forward(self, X): hidden_matrix = self.sigmoid(self.W1.matmul(X) + self.b1.reshape(-1, 1)) output_matrix = self.sigmoid(self.W2.matmul(hidden_matrix) + self.b2.reshape(-1, 1)) return output_matrix

然后有训练过程:

from torch import optim Net = SAE() # Net(torch.tensor(data[0], dtype=torch.float)) # 开始训练 optimizer = optim.SGD(Net.parameters(), lr=0.05) Loss_func = nn.MSELoss(reduction="sum") epochs = 128 for _ in range(epochs): lenght = data.shape[0] Loss = 0 for i in range(lenght): X = torch.tensor(data[i]) X_hat = Net(X) loss = Loss_func(X_hat, X)/2 optimizer.zero_grad() loss.backward() optimizer.step() Loss += loss print(f"{_+1} loss:{Loss/lenght}")

为了验证上述梯度回传计算的正确性,使用这样的代码来验证,已经将矩阵都以全0的方式初始化:

input_data = data[0] # 基于Numpy的方法 w1, b1, w2, b2= W1, B1, W2, B2 output, hidden_layer_output=forward_propagation(input_data,w1,b1,w2,b2) dw1, dw2, db1, db2 = gradiengt_decent(output,input_data,w2,hidden_layer_output) # 基于Pytoch的方法 Net = SAE() X = torch.DoubleTensor(data[0]) X_hat = Net(X) loss = Loss_func(X_hat, X)/2 optimizer.zero_grad() loss.backward() # 比较 print(Net.W1.grad - dw1) print(Net.W2.grad - dw2) print(Net.b1.grad - db1) print(Net.b2.grad - db2)

结果有:

tensor([[0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0.]], dtype=torch.float64) tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dtype=torch.float64) tensor([0., 0., 0.], dtype=torch.float64) tensor([ 0.0000e+00, 0.0000e+00, 0.0000e+00, 0.0000e+00, -2.7756e-17, 2.7756e-17, 0.0000e+00, 0.0000e+00], dtype=torch.float64)

说明上述的计算基本正确,可能存在一些精度上的误差。

至此,我们就可以开始分析,题主所说的“神经网络输出都一样这个问题”。

首先先看题主的任务,可能是某种矩阵压缩的任务,将矩阵从(8,8)的形式压缩到(3,8)的形式,甚至可能是8个8维度的行向量,压缩成3个8维度的行向量以探求向量之间的关系。

如果是后者,从线性代数的秩来看,是比较难做到的,一般来说,随机出来的矩阵是一个满秩矩阵,很难存在可以压缩的信息,即从8x8压缩到3x8的形式。

但是至少,通过实验结果,以及可能的分析,可以看到,题主的任务,首先在正确的实现下并不是0.5附近徘徊,在训练了1000次以后有:

1000 loss:2.3343 Net(torch.tensor(data[0]).cuda()).cpu().detach().numpy() array([[0.51172373, 0.56241952, 0.55800087, 0.47174427, 0.56678227, 0.40300673, 0.44959767, 0.52739377], [0.48013683, 0.40617486, 0.41255567, 0.53892418, 0.39989175, 0.63824329, 0.57131044, 0.45713635], [0.52591279, 0.61318884, 0.60572179, 0.45605942, 0.62052586, 0.33946687, 0.41771344, 0.553189 ], [0.50419805, 0.51642133, 0.51534773, 0.49460804, 0.51748356, 0.47785564, 0.48926727, 0.50795945], [0.52419569, 0.5990492 , 0.5926021 , 0.46454659, 0.60539538, 0.36360818, 0.43164683, 0.54750019], [0.52763252, 0.60983392, 0.6027865 , 0.46186592, 0.61676286, 0.35126886, 0.42565517, 0.55329581], [0.51289696, 0.5519445 , 0.54853156, 0.48214023, 0.55531696, 0.42882915, 0.46505109, 0.52494809], [0.39894411, 0.12901825, 0.14456692, 0.68272197, 0.1150574 , 0.94388547, 0.80557175, 0.29499562]])

可以看到部分数值有0.9和0.29不在0.5附近的存在,但是损失函数此时已经不随着训练而继续下降了,一定程度说明当前的模型的性能比较差,可能无法达到题主的目的。

扫描二维码推送至手机访问。

版权声明:本文由卡卷网发布,如需转载请注明出处。

本文链接:https://www.kajuan.net/ttnews/2025/02/11092.html

分享给朋友:

相关文章

中国CEO直播简史

中国CEO直播简史

  领导人爱对外发言这事,是中国的一个传统文化。  从乾隆爱往名画上盖章上,就可见一斑。  3Q大战时代,微博成了大佬“吵架”的战场,饭否变成了部分爱面子者的日记。  某种程度上说,互联网公关行业的诞生,和大佬们爱发言有着直接的关系。只不过...

个人站长为什么越来越少了?个人网站还能做吗?

个人站长为什么越来越少了?个人网站还能做吗?

在互联网快速发展的浪潮中,个人站长曾经有过辉煌的时代,但如今却面临着诸多困境,甚至走向 “死亡”。一、竞争激烈,难以立足如今的互联网行业,巨头林立,各大平台凭借着雄厚的资金、技术和资源优势,占据了绝大部分市场份额。无论是搜索引擎、社交媒体还...

电脑c盘哪些文件可以删除?

电脑c盘哪些文件可以删除?

电脑上的文件夹都是英文,很多朋友都不敢乱删,下面这几个文件夹里的文件,你可以放心删除。一、可删除的文件1、Backup这是一个备份文件夹,很多装机软件经常会把需要备份的东西,放在这个文件夹中。而当我们需要的软件正常保存之后,这些东西也就没有...

b站真的能自学PS吗?

b站真的能自学PS吗?

看你想达到哪一种程度了,如果你只是平常用PS扣图、调整照片大小、尺寸、简单调个色这样,自学真的挺简单的,B站很多免费的教程都可以教会你这些技巧。但是如果说你想成为专业的设计师或者是商业修图师,无师自通真的非常难,首先你会走很多弯路,不知道怎...

计算机专业不干互联网不热爱技术,还能转行干什么?

转行的思路,无非也就是那几个。我们顺着每个思路,一路捋一遍,基本上,大致可行的方向,也就有了。一、跟对口职业和岗位业务链条相邻的职业和岗位计算机专业如果找到了对口的技术岗位,跟技术工作联系最紧密的岗位是什么?产品经理。当然,大多数产品经理也...

为什么扫码支付在中国流行,在发达国家被排斥?

因为这是一种落后的技术。卖菜的大爷花5毛钱就可以打印出一张二维码来接受付款。你觉着这种先进么?跟先进完全不沾边的。正是因为不先进,所以才能流行。卖菜大爷用不起一台先进的、具有NFC感应功能的、还能刷各种银行卡的收款机。这就是现实。发达国家,...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。