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

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

卡卷网12个月前 (02-22)每日看点185

看了一下代码,确实有点东西嗷,自己写神经网络,自己写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

分享给朋友:

相关文章

如果我们没有自己的手机操作系统,会出现所谓的“卡脖子”“安全”问题吗?

如果我们没有自己的手机操作系统,会出现所谓的“卡脖子”“安全”问题吗?

前段时间某某高管在微博发文表示:“其实我觉得,中国人需要的不是一个自己的手机os,而是一个全国产的微信,再搭配一些辅助功能。”这算是“安卓开源”开源的代表了吧。然而打脸来的如此之快,10月30日消息,在Linux内核疑似大规...

知乎创作收益高吗?

知乎创作收益高吗?

高啊,主要是大多数人不知道怎么做。(这是以前写的,请看到最后)我之前就是傻傻写问答,文章,结果一毛钱收益没有。知乎上收益高的主要有盐选,好物推荐,致知计划,付费咨询。1、盐选吧,是写故事,小说类的天堂,还有就是大咖们的地盘了,难度高。2、好...

如何在自己家里建立一套私有云系统?需要哪些设备?

如何在自己家里建立一套私有云系统?需要哪些设备?

我敢保证,这绝对是目前为止最简单的搭建家用私有云的方法:“一台主机+至少一块硬盘”足矣!不需要任何专业知识,也没有复杂繁琐的步骤,十分钟不到就能搭建好,帮你成功打开文件云储存新世界的大门!还在单纯依靠网盘进行文件存储的朋友,不是我吐槽哈,它...

腾讯文档回收站彻底删除文件真的找不回来了吗?

趁早打电话联系腾讯文档的人可能还有救,一般这种都是数据库里标记为删除,文件还没有实际删除,然后经过一段时间后程序统一进行真删除。这个“一段时间”可长可短,可能是一小时也可能是几天几个月甚至几年,要看腾讯服务器的程序是怎么写的。不过你联系腾讯...

都是前端框架,为什么用 React 的人会有优越感?

都是前端框架,为什么用 React 的人会有优越感?

上家公司一直搞react,最近第一次写个vue3项目。老板朋友来了看了下页面说:这用vue写的吧。我:是...老板朋友:一看就知道是vue做的。这tm你看一眼页面就知道用的啥框架?这感觉就是,我一直用苹果,偶尔用了下华为打电话,有个人离老远...

用wordpress做这个网站的话,需要用到哪些插件?

一个完整成型的B2C电商独立站,如果用wordpress+woocommerce搭建,最终会用上20来个插件,而且是在经过慎重筛选,剔除不必要的插件的情况下,别问我为什么。wordpress建站,插件安装多了,速度会变慢,这是常识,但需要在...

发表评论

访客

看不清,换一张

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