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

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

卡卷网10个月前 (02-22)每日看点164

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

分享给朋友:

相关文章

k70和红米note14pro+咋选啊?

k70和红米note14pro+咋选啊?

首选红米K70,可满足题主的功能需求。红米 Note 14 pro+的使用者定位并不是学生,红米K70的使用者定位才是学生。一、k70和红米note14pro+各自的参数红米K70推出的定位初衷,是主打性能为主。最佳使用对象是喜欢游戏者,...

Photoshop 有哪些使用技巧?

Photoshop 有哪些使用技巧?

不看后悔系列!本篇分享25个PS实用的技巧!不能保证每个都能让你惊艳,但是却是我用心写出来的,希望对你有帮助。另外我的知乎也写了接近200篇PS的技巧,超级合集分享!分享25个关于PS的技巧一、快速制作文字倒影1、新建文档,1500×100...

你是如何在不依靠工资收入的情况下赚到一万元的?

你是如何在不依靠工资收入的情况下赚到一万元的?

分享几个路子稳,门槛低,变现快,适合年轻人的靠谱的副业。绝对不是送外卖、滴滴、搬砖等等的苦力活,这几个副业都是能是性价比极高,还能让你快速成长的工作。想通过副业实现暴富、立马月入过万的同学,建议速速划走。今天撇哥就给大家分享100+个靠谱赚...

中国芯片产量达1399亿颗,这意味着什么?

美国并不是没有明白人,只是特朗普不懂芯片产业的情况,冒冒失失在ZZ正确下,开启了对中国芯片的掐脖子,结果没掐死,反而让人练出了铁肺。芯片是所有科技产业的上游,美国原本在上游呆得很舒服,靠英伟达、AMD、德州仪器、高通等这些几十年霸主地位的公...

如何看待 Luv Letter 表示影视飓风专业性实在是让人失望?

一点儿新进展,另外以下有部分需要关注对于统一变量的问题。这个我在对比上面有写小字解释,我在一开始使用ffmpeg来控制变量。但是发现了不少问题,比如因为遗留bug,会强制将非整数帧的素材转码为VFR导致无法正确帧间对比,因此不考虑,同时尽量...

客观的讲,华为是不是真的遥遥领先?

客观的讲,华为是不是真的遥遥领先?

最近,华为上市了最新款的三折叠屏手机,于是很多大V们把这款手机吹上了天,吹成是技术上遥遥领先于全世界。谁要是敢质疑华而不实,就会被人扣上一顶汉奸卖国贼美狗的帽子。把一个商品捧成了宗教和菩萨,你只能说好,不能说不好。其实这个世界上,只要是人和...

发表评论

访客

看不清,换一张

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