【不使用深度学习框架】多层感知机实现手写Minist数据集识别

手写Minist识别是一个非常经典的问题,其数据集共有70000张28*28像素的图片,其中60000张作为训练集,剩下的10000张作为测试集,每一张图片都表示了一个手写数字,经过了灰度处理。
本文延续前面文章提到的多层感知机,在这里完成更复杂的任务,这个也可以作为分类任务的代表。

1.导入数据

首先导入模型,我个人的习惯是在同名目录下新建static文件夹存放一些数据,这里先建好static/data,然后就可以等待下载数据了,文件夹关系如下:
在这里插入图片描述

在这里,尽管导入的时候已经是batch的形式了,为了后面方便起见,我还将训练集和测试集分别按照numpy的格式组装为一个个batch,然后将标注进行独热编码,具体的代码可以看后面的完整代码。

2.训练模型


训练模型是最复杂的部分,这里涉及到前向传播和反向传播,这部分在我前面的博客中提到过,不再赘述,不同的是多出了在每个隐藏层和输出层进行了批量规范化(Batch Normalization),也就是BN层,在这里简单展开介绍一下。
(感觉我自己写的部分可能只能我自己看得懂了…建议可以顺着我推荐的博客或者其他人的好好了解一下,我写的比较简单)

建议先看这两篇:
1. 知乎,这一篇主要是介绍一些优点

2. 知乎,这一篇的推导非常详细
​先考虑在训练时的情况,给出表达式,假设输入为 x x x,输出为 o o o,维度均为 n × 1 n\times1 n×1,那么在 B N BN BN层中经过了如下变换:
μ = 1 m ∑ i = 1 n x i σ 2 = 1 m ∑ i = 1 n ( x i − μ ) 2 x ^ = x − μ σ 2 + ϵ o = γ x ^ + β μ=\frac{1}{m}∑_{i=1}^{n}x_i\\\sigma^2=\frac{1}{m}∑_{i=1}^{n}(x_i-μ)^2\\\hat{x}=\frac{x-μ}{\sqrt{\sigma^2+\epsilon}}\\o=\gamma \hat{x}+\beta μ=m1i=1nxiσ2=m1i=1n(xiμ)2x^=σ2+ϵ xμo=γx^+β
μ , σ μ,\sigma μ,σ分别是均值和方差,下面的 x ^ \hat{x} x^也就是经过标准化的 x x x,加入最后的 γ , β \gamma,\beta γ,β是缩放和平移的系数,通过学习得到

得到了表达式,那么下面讨论参数更新的问题,也就是反向传播,不考虑 o o o之后到损失函数的,因为之后的都可以用 ∂ L ∂ o i \frac{ \partial L}{ \partial o_i} oiL给出。

先给出 γ \gamma γ β \beta β的:
∂ o i ∂ γ = x ^ i ∂ o i ∂ β = 1 \frac{ \partial o_i}{ \partial \gamma}=\hat{x}_i\\ \frac{ \partial o_i}{ \partial \beta}=1 γoi=x^iβoi=1
由于输入是 x x x,这个和前面的层直接关联,因此输出 o o o x x x的关系也很重要,计算过程如下:
∂ o i ∂ x i = ∂ μ ∂ x i ∂ o i ∂ μ + ∂ σ 2 ∂ x i ∂ o i ∂ σ 2 + ∂ x ^ i ∂ x i ∂ o i ∂ x ^ i \frac{ \partial o_i}{ \partial x_i}= \frac{ \partial μ}{ \partial x_i}\frac{ \partial o_i}{ \partial μ}+ \frac{ \partial \sigma^2}{ \partial x_i}\frac{ \partial o_i}{ \partial \sigma^2}+ \frac{ \partial \hat{x}_i}{ \partial x_i}\frac{ \partial o_i}{ \partial \hat{x}_i} xioi=xiμμoi+xiσ2σ2oi+xix^ix^ioi
这里要注意的是有三个关于 x x x的变量的链式求导, x x x在关于 x , σ 2 , x ^ x,\sigma^2,\hat{x} x,σ2,x^的表达式中都出现了,因此链式求导都要加上,这在具体计算 ∂ o i ∂ μ , ∂ x ^ i ∂ x i \frac{ \partial o_i}{ \partial μ},\frac{ \partial \hat{x}_i}{ \partial x_i} μoi,xix^i等都要注意,下面写一个 ∂ o i ∂ μ \frac{ \partial o_i}{ \partial μ} μoi的:
∂ o i ∂ μ = ∂ x ^ i ∂ μ ∂ o i ∂ x ^ i + ∂ σ 2 ∂ μ ∂ o i ∂ σ 2 \frac{ \partial o_i}{ \partial μ}= \frac{ \partial \hat{x}_i}{ \partial μ} \frac{ \partial o_i}{ \partial \hat{x}_i}+ \frac{ \partial \sigma^2}{ \partial μ} \frac{ \partial o_i}{ \partial \sigma^2} μoi=μx^ix^ioi+μσ2σ2oi
这里之所以还有关于 σ 2 \sigma^2 σ2是因为 σ \sigma σ的表达式直接出现了 μ μ μ

下面是一些过程,实际的使用是和损失函数 L L L的关系,下面框出的是看不懂的,去问了,不知道作者会不会回复。

具体的过程这里不写了,就直接抄博客的最简表达式了(因为太抽象了…)

他的表达式如下:
在这里插入图片描述

在这样的情况下,原本更新 w w w的都要变化。假设经过线性累加后为 N N N,激活后为 O O O,那么原本 L L L关于 w w w的链式传播为:
∂ L ∂ w = ∂ N ∂ w ∂ O ∂ N ∂ L ∂ O \frac{ \partial L}{ \partial w}= \frac{ \partial N}{ \partial w} \frac{ \partial O}{ \partial N} \frac{ \partial L}{ \partial O} wL=wNNOOL
现在在 O O O N N N之间多出了 B N BN BN层,那么链式传播就要发生变化,假设 B N BN BN层为 B B B先经过 B N BN BN层然后经过激活函数(一般对于ReLU是这个顺序),那么有下式:
∂ L ∂ w = ∂ N ∂ w ∂ B ∂ N ∂ O ∂ B ∂ L ∂ O \frac{ \partial L}{ \partial w}= \frac{ \partial N}{ \partial w} \frac{ \partial B}{ \partial N} \frac{ \partial O}{ \partial B} \frac{ \partial L}{ \partial O} wL=wNNBBOOL
先看 ∂ O ∂ B ∂ L ∂ O \frac{ \partial O}{ \partial B} \frac{ \partial L}{ \partial O} BOOL,对于反向传播来说,输入到底是 B B B(现在)还是 N N N(原来)都不重要,因为这个式子的计算不涉及这个值,只和输出 O O O有关,因此可以等同于原来的损失 δ \delta δ
∂ O ∂ B ∂ L ∂ O = δ L a y e r , j = { g ( O L a y e r , j ) ∑ k = 1 K w j k δ L a y e r + 1 , k , L a y e r 不为输出 g ( O L a y e r , j ) ( O L a y e r , j − l a b e l k ) , L a y e r 为输出 \frac{ \partial O}{ \partial B} \frac{ \partial L}{ \partial O}=δ_{Layer,j}= \left\{ \begin{array}{ccc} g(O_{Layer,j})∑_{k=1}^{K}w_{jk}δ_{Layer+1,k},\;Layer不为输出 \\ g(O_{Layer,j})(O_{Layer,j}-label_k),\;\;\;Layer为输出 \end{array} \right.\\ BOOL=δLayer,j={g(OLayer,j)k=1KwjkδLayer+1,k,Layer不为输出g(OLayer,j)(OLayer,jlabelk),Layer为输出
这里的 g ( x ) g(x) g(x)表示激活函数的导数。

写到这一步,其实上面最终表达式的 ∂ L ∂ x i \frac{ \partial L}{ \partial x_i} xiL已经可以表示了,他的 x i x_i xi其实就表示这里的 N N N,也就是 B N BN BN层的输入,写出表达式的等效:(为了和上面一致,把 x i x_i xi写为 x x x
∂ L ∂ x = ∂ B ∂ N ∂ L ∂ B = ∂ B ∂ N δ = f ( ∂ B ∂ x ^ δ ) = f ( γ δ ) \frac{ \partial L}{ \partial x}= \frac{ \partial B}{ \partial N} \frac{ \partial L}{ \partial B}=\frac{ \partial B}{ \partial N}δ=f(\frac{ \partial B}{ \partial \hat{x}}δ)=f(\gamma δ) xL=NBBL=NBδ=f(x^Bδ)=f(γδ)
这个 f ( x ) f(x) f(x)就是上面的求解公式,将 γ δ \gamma \delta γδ替换掉 ∂ L ∂ x ^ \frac{ \partial L}{ \partial \hat{x}} x^L即可。

其他的其实跟前面提到的神经网络没什么区别了,在代码中我把隐藏层的偏置都去掉了,因为有的博客提到了BN层的偏置就不需要隐藏层的偏置了。(正好这个偏置又很难写)

在真正预测的时候,这时候无法计算出批对应的均值和标准差,这里采用的是统计训练过程中的均值 μ μ μ和方差 σ 2 \sigma^2 σ2,用于预测时进行规范化。

3.模型运行

模型运行无论之后的结果:(显示出来的都是预测的前面五个的结果)
在这里插入图片描述

因为没有很多优化,代码运行的效率并不算好,只能说勉强能跑(在 batch_size = 32 的情况下大约两分钟训练完一轮),在训练完第一轮的时候在测试集的准确率就已经达到大约90%了,后面几轮的提升不大。
没试过其他batch_size或者调参之类的,能跑应该就是没问题的吧?
在这里插入图片描述

4.完整代码

下面贴出完整代码,写的有冗余,写法也很抽象,仅供学习参考哈(很多推导我在前面的博客都提到过,这一篇就没有多说了)

# 手写Minist数据集分类
import numpy as np
import torch
import torchvision
from matplotlib import pyplot as plt
from numpy import log
from numpy.random import uniform
from sklearn.metrics import accuracy_score
from tqdm import tqdmplt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = Falsedef load_data(if_reload=False):batch_size_train = 32batch_size_test = 1random_seed = 1torch.manual_seed(random_seed)train_loader = torch.utils.data.DataLoader(torchvision.datasets.MNIST('./static/data/', train=True, download=True,transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize((0.1307,), (0.3081,))])),batch_size=batch_size_train, shuffle=True)test_loader = torch.utils.data.DataLoader(torchvision.datasets.MNIST('./static/data/', train=False, download=True,transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor(),torchvision.transforms.Normalize((0.1307,), (0.3081,))])),batch_size=batch_size_test, shuffle=True)train_size = len(train_loader)test_size = len(test_loader)print('训练规模:', train_size)print('测试规模:', test_size)# 设置类别的数量num_classes = 10onehot = np.eye(num_classes)X_train = np.zeros((train_size, batch_size_train, 784, 1), dtype=np.float32)y_train = np.zeros((train_size, batch_size_train, 10, 1), dtype=int)  # 要独热编码X_test = np.zeros((test_size, batch_size_test, 784, 1), dtype=np.float32)y_test = np.zeros((test_size, batch_size_test, 1), dtype=int)  # 不需要进行独热编码# 训练for batch_idx, (data, target) in enumerate(train_loader):if data.shape[0] < batch_size_train:breakX_train[batch_idx] = np.array(data).reshape(batch_size_train, -1, 1)  # 展平放入y_train[batch_idx] = onehot[target].reshape(batch_size_train, -1, 1)  # 将整数转为一个10位的one hot编码# 测试for batch_idx, (data, target) in enumerate(test_loader):if data.shape[0] < batch_size_test:breakX_test[batch_idx] = np.array(data).reshape(batch_size_test, -1, 1)  # 展平放入y_test[batch_idx] = np.array(target).reshape(batch_size_test, -1, 1)return X_train, y_train, X_test, y_testclass MyNet():def __init__(self):self.eps = 0.00001  # 极小值self.new_rate = 0.1  # 均值新值所占的比例self.alpha_BN = 0.005# sigmoiddef sigmoid(self, x):return 1 / (1 + np.exp(-x)) + self.eps  # 加上一个极小值# ruledef rule(self, x):x[np.where(x < 0)] = 0return x# 损失函数def loss_func(self, output, label):  # 输入是batch*列向量out = log(output + self.eps)  # 加上一个极小值loss = -label.transpose(0, 2, 1) @ outreturn np.sum(loss.flatten())  # 总的损失(batch个)def softmax(self, out):  # 输入格式(batch,row,1),应该算出每个batch的最大值,然后减掉x = out.copy()res = np.zeros_like(x, dtype=np.float32)for i in range(x.shape[0]):x[i] -= max(x[i])expx = np.exp(x[i])sexpx = np.sum(expx)res[i] = expx / sexpxreturn res# batch normdef train_normlize(self, x, gamma, beta):  # 输入 (batch,rows,1)mmu = np.mean(x, axis=0)var = np.mean((x - mmu) ** 2, axis=0)  # rows,1ivar = 1 / np.sqrt(var + self.eps)  # rows,1x_hat = (x - mmu) * ivar  # [(batch,rows,1)-(rows,1)]/rows,1->batch,rows,1out = gamma * x_hat + betareturn out, ivar, mmu, var, x_hat  # 后面mmu和var是为了累计总均值和标准差,所以传回,x_hat在更新系数γ需要用到def normlize_eval(self, x):  # 输入 (batch,rows,1)return (x - self.running_mmu) / self.running_vardef fit(self, X, Y, test_x=None, test_y=None, epochs=30, test_epoch=1 ,alpha=0.01, layers=2, layer_size=3, activation='sigmoid'):# layer,layer_size:中间的隐藏层层数和隐藏层的大小print('fit ...')self.acti_func = self.sigmoidif activation == "rule":self.acti_func = self.ruleelse:assert "unknown activation algorithm: %s" % (activation)batch_size, in_features = X[0].shape[0], X[0].shape[1]out_features = Y[0].shape[1]batchs = len(X)batch_size = X[0].shape[0]  # batch大小print('batchs:', batchs)samples = batch_size * batchs# 矩阵初始化(随机初始化)w_first = uniform(-1, 1, (in_features, layer_size))  # 每一行是一个输入神经元对所有下一层神经元的权值,+1是偏置的权值(有BN层不需要)w_last = uniform(-1, 1, (layer_size, out_features))  # 每一行是一个输入神经元对所有下一层神经元的权值self.w = None  # 防止后续报错if layer_size > 1:  # 刚好等于1就不需要了w = uniform(-1, 1, (layers - 1, layer_size, layer_size))  # 每一层都大小+1# 运行过程中的变量(注意有了BN层不需要偏置了)delta = np.zeros((batch_size, layers, layer_size, 1), dtype=float)  # 前面每一层都要+1delta_last = np.zeros((batch_size, out_features, 1), dtype=float)  # 每一列是一层的δout_last = np.ones((batch_size, out_features, 1), dtype=float)  # 每一列是一层的outputout = np.ones((batch_size, layers, layer_size, 1), dtype=float)out_softmax = np.ones((batch_size, out_features, 1), dtype=float)  # 经过softmax得到的结果norm_gamma = np.ones((layers, layer_size, 1), dtype=float)norm_gamma_last = np.ones((out_features, 1), dtype=float)  # BN的γnorm_beta = np.zeros((layers, layer_size, 1), dtype=float)norm_beta_last = np.zeros((out_features, 1), dtype=float)  # BN的βivar = np.zeros((layers, layer_size, 1), dtype=float)  # 1/sqrt(σ^2+eps)ivar_last = np.zeros((layers, out_features, 1), dtype=float)  # 1/sqrt(σ^2+eps)x_hat = np.zeros((batch_size, layers, layer_size, 1), dtype=float)x_hat_last = np.zeros((batch_size, out_features, 1), dtype=float)  # 每一列是一层的output# 下面是运行过程中需要统计的均值和标准差running_mmu = np.zeros((layers, layer_size, 1), dtype=float)running_mmu_last = np.zeros((out_features, 1), dtype=float)running_var = np.zeros((layers, layer_size, 1), dtype=float)running_var_last = np.zeros((out_features, 1), dtype=float)# 开始迭代loss_list = []for epoch in range(1, 1 + epochs):loss = 0cnt = 0pbar = tqdm(zip(X, Y))for idx, (x, y) in enumerate(pbar):# 前向传播# 输入->...layers->layer_-1->softmax->argmax()for layer in range(layers + 2):# 先标准化再激活if layer == 0:  # 第一层xx = (w_first.T @ x)out[:, layer, :], tmp, mmu, var, x_hat_ = self.train_normlize(x=xx, gamma=norm_gamma[layer], beta=norm_beta[layer])out[:, layer, :] = self.acti_func(out[:, layer, :])x_hat[:, layer, :] = x_hat_ivar[layer] = tmprunning_var[layer] = running_var[layer] * (1 - self.new_rate) + var * self.new_raterunning_mmu[layer] = running_mmu[layer] * (1 - self.new_rate) + mmu * self.new_rateelif layer < layers:  # 不是最后一层xx = (w[layer - 1].T @ out[:, layer - 1])out[:, layer, :], tmp, mmu, var, x_hat_ = self.train_normlize(x=xx, gamma=norm_gamma[layer], beta=norm_beta[layer])out[:, layer, :] = self.acti_func(out[:, layer, :])x_hat[:, layer, :] = x_hat_ivar[layer] = tmprunning_var[layer] = running_var[layer] * (1 - self.new_rate) + var * self.new_raterunning_mmu[layer] = running_mmu[layer] * (1 - self.new_rate) + mmu * self.new_rateelif layer == layers:  # 最后一层隐藏层到输出层xx = w_last.T @ out[:, layer - 1]out_last[:, :, :], tmp, mmu, var, x_hat_ = self.train_normlize(x=xx, gamma=norm_gamma_last, beta=norm_beta_last)out_last = self.acti_func(out_last)ivar_last = tmpx_hat_last[:, :, :] = x_hat_running_var_last = running_var_last * (1 - self.new_rate) + var * self.new_raterunning_mmu_last = running_mmu_last * (1 - self.new_rate) + mmu * self.new_rateelse:out_softmax = self.softmax(out_last)# 反向传播for layer in range(layers, -1, -1):  # layers,...,0# 计算出每一层的损失if layer == layers:  # 最后一层(输出层),这里直接用softmax层计算得到# print('输出层')if activation == 'sigmoid':delta_last = out_last * (1 - out_last) * (out_softmax - y)# 分类的导数实际和回归的一样,只不过多了个softmax过程,实际上还是一样的elif activation == 'rule':deriva = np.ones_like(out_last)deriva[np.where(out_last < 0)] = 0delta_last = deriva * (out_softmax - y)elif layer == layers - 1:  # 隐藏层最后一层,连接输出层# print('最后一层隐藏')if activation == 'sigmoid':delta[:, layer] = out[:, layer] * (1 - out[:, layer]) * (w_last @ delta_last)elif activation == 'rule':deriva = np.ones_like(out[:, layer])deriva[np.where(out[:, layer] < 0)] = 0delta[:, layer] = deriva * (w_last @ delta_last)else:  # 最后一层# print('隐藏')if activation == 'sigmoid':delta[:, layer] = out[:, layer] * (1 - out[:, layer]) * (w[layer] @ delta[:, layer + 1])  # 只有1-n会继续前向传播elif activation == 'rule':deriva = np.ones_like(out[:, layer])deriva[np.where(out[:, layer] < 0)] = 0delta[:, layer] = deriva * (w[layer] @ delta[:, layer + 1])# 更新系数(w和两个BN参数)for layer in range(layers + 1):if layer == 0:  # 输入-隐藏# print('输入-隐藏')x_hat_ = x_hat[:, layer, :]  # x_hat少一行1(相比x)det_gamma = np.sum(x_hat_ * delta[:, layer, :], axis=0)  # batch,rows-1,1 x rows-1,1det_beta = np.sum(delta[layer, :], axis=0)  # beta其实跟b是一样的(本来有了BN层的偏置就不需要b了)var_sqr = ivar[layer]  # rows,1dxhat = norm_gamma[layer] * delta[:, layer, :]  # γ(do/dx_hat)*δ(dL/do)dx = 1 / batch_size * var_sqr * (batch_size * dxhat - np.sum(dxhat, axis=0) -x_hat_ * np.sum(dxhat * x_hat_, axis=0))det_w = x @ dx.transpose([0, 2, 1])det_w = np.mean(det_w, axis=0)w_first = w_first - alpha * det_w  # O_{Layer-1,i}δ_{Layer,j}norm_gamma[layer] -= self.alpha_BN * det_gammanorm_beta[layer] -= self.alpha_BN * det_betaelif layer < layers:  # 隐藏-隐藏# print('隐藏-隐藏')x_hat_ = x_hat[:, layer, :]  # x_hat少一行1(相比x)det_gamma = np.sum(x_hat_ * delta[:, layer, :], axis=0)  # batch,rows-1,1 x rows-1,1det_beta = np.sum(delta[layer, :], axis=0)  # beta其实跟b是一样的(本来有了BN层的偏置就不需要b了)var_sqr = ivar[layer]  # rows,1dxhat = norm_gamma[layer] * delta[:, layer, :]  # γ(do/dx_hat)*δ(dL/do)dx = 1 / batch_size * var_sqr * (batch_size * dxhat - np.sum(dxhat, axis=0) -x_hat_ * np.sum(dxhat * x_hat_, axis=0))det_w = out[:, layer - 1] @ dx.transpose([0, 2, 1])det_w = np.mean(det_w, axis=0)w[layer - 1] = w[layer - 1] - alpha * det_w  # O_{Layer-1,i}δ_{Layer,j}norm_gamma[layer] -= self.alpha_BN * det_gammanorm_beta[layer] -= self.alpha_BN * det_betaelse:  # 隐藏-输出# print('隐藏-输出')x_hat_ = x_hat_last  # x_hat少一行1(相比x)det_gamma = np.sum(x_hat_ * delta_last, axis=0)  # batch,rows-1,1 x rows-1,1det_beta = np.sum(delta_last, axis=0)  # beta其实跟b是一样的(本来有了BN层的偏置就不需要b了)var_sqr = ivar_last  # rows,1dxhat = norm_gamma_last * delta_last  # γ(do/dx_hat)*δ(dL/do)dx = 1 / batch_size * var_sqr * (batch_size * dxhat - np.sum(dxhat, axis=0) -x_hat_ * np.sum(dxhat * x_hat_, axis=0))det_w = out[:, layer - 1] @ dx.transpose([0, 2, 1])det_w = np.mean(det_w, axis=0)w_last = w_last - alpha * det_w  # O_{Layer-1,i}δ_{Layer,j}norm_gamma_last -= self.alpha_BN * det_gammanorm_beta_last -= self.alpha_BN * det_betasoft_fal = np.argmax(out_softmax, axis=1).flatten()y_fal = np.argmax(y, axis=1).flatten()cnt += np.where(soft_fal == y_fal)[0].sizethis_loss = self.loss_func(out_softmax, y)loss += this_loss / batch_size  # 平均每个样本误差print('当前训练轮整体准确率:%.2f%%' % (100 * cnt / (batch_size * len(X_train))))loss_list.append(loss / samples)  # 样本平均误差print('epoch %d loss:%.6f' % (epoch, loss / samples))running_var *= batch_size / (batch_size - 1)running_var_last *= batch_size / (batch_size - 1)  # 无偏估计预测# 保存参数self.w_first = w_firstself.w_last = w_lastself.w = wself.in_features = in_featuresself.out_features = out_featuresself.layers = layersself.layer_size = layer_sizeself.running_mmu = running_mmuself.running_mmu_last = running_mmu_lastself.running_var = running_varself.running_var_last = running_var_lastself.norm_gamma = norm_gammaself.norm_gamma_last = norm_gamma_lastself.norm_beta = norm_betaself.norm_beta_last = norm_beta_lastif epoch % test_epoch == 0:  # 一轮测试一次if test_x is None or test_y is None:continueprint('(!) test...')pred = self.predict(test_x)display_num = 5  # 显示的个数for i in range(display_num):print('pred=', pred[i], ' true=', test_y[i])acc = accuracy_score(test_y.reshape(-1, 1), pred.reshape(-1, 1))print('acc=%.2f' % (acc))plt.plot(loss_list)plt.show()def predict(self, X):print('test size:',X.shape)w_first = self.w_firstw_last = self.w_lastw = self.wlayers = self.layersout_features = self.out_featureslayer_size = self.layer_sizebatch_size = X.shape[1]  # 得到batch的大小running_mmu = self.running_mmurunning_mmu_last = self.running_mmu_lastrunning_var = self.running_varrunning_var_last = self.running_var_lastnorm_gamma = self.norm_gammanorm_gamma_last = self.norm_gamma_lastnorm_beta = self.norm_betanorm_beta_last = self.norm_beta_lastout_last = np.ones((batch_size, out_features, 1), dtype=float)  # 每一列是一层的outputout = np.ones((batch_size, layers, layer_size, 1), dtype=float)out_softmax = np.ones((batch_size, out_features, 1), dtype=float)  # 经过softmax得到的结果res = np.zeros((X.shape[0], batch_size, 1), dtype=int)for idx, x in enumerate(X):# 前向传播for layer in range(layers + 2):if layer == 0:  # 第一层out[:, layer, :] = ((w_first.T @ x) - running_mmu[layer, :]) / np.sqrt(running_var[layer, :] + self.eps)out[:, layer, :] = norm_gamma[layer] * out[:, layer, :] + norm_beta[layer]out[:, layer, :] = self.acti_func(out[:, layer, :])elif layer < layers:  # 不是最后一层out[:, layer, :] = ((w[layer - 1].T @ out[:, layer - 1]) - running_mmu[layer, :]) / np.sqrt(running_var[layer, :] + self.eps)out[:, layer, :] = norm_gamma[layer] * out[:, layer, :] + norm_beta[layer]out[:, layer, :] = self.acti_func(out[:, layer, :])elif layer == layers:  # 最后一层隐藏层到输出层out_last = (w_last.T @ out[:, layer - 1] - running_mmu_last) / np.sqrt(running_var_last + self.eps)out_last = norm_gamma_last * out_last + norm_beta_lastout_last = self.acti_func(out_last)else:out_softmax = self.softmax(out_last)output = np.argmax(out_softmax, axis=1)res[idx] = outputreturn resif __name__ == '__main__':print('******** minist手写数据集分类 *********')X_train, y_train, X_test, y_test = load_data(if_reload=True)  # 获取标准化后的数据myNet = MyNet()myNet.fit(X_train, y_train, test_x=X_test, test_y=y_test, epochs=5, test_epoch=1, alpha=0.01, layers=2, layer_size=200,activation='rule')print('训练完毕')print('test...')pred = myNet.predict(X_test)display_num = 5  # 显示的个数for i in range(display_num):print('pred=', pred[i], ' true=', y_test[i])acc = accuracy_score(y_test.reshape(-1, 1), pred.reshape(-1, 1))print('acc=%.2f' % (acc))

差不多就是这样了,有什么问题的话可以欢迎指出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/832004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Osek网络管理测试】[TG1_TC12]网络管理报文ID范围

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT可识别的网络管理报文NMID(0x400~0x46F) 3.测试…

从一到无穷大 #26 Velox:Meta用cpp实现的大一统模块化执行引擎

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言业务案例PrestoSparkXStreamDistributed messaging systemData IngestionData Pr…

JavaScript的操作符运算符

前言&#xff1a; JavaScript的运算符与C/C一致 算数运算符&#xff1a; 算数运算符说明加-减*乘%除/取余 递增递减运算符&#xff1a; 运算符说明递增1-- 递减1 补充&#xff1a; 令a1&#xff0c;b1 运算a b ab12ab22ab--10a--b00 比较(关系)运算符&#xff1a; 运算…

(优作)基于STM32 人群定位、调速智能风扇设计(程序、设计报告、视频演示)

引言 当今生活中&#xff0c;风扇已成为人们解暑的重要工具&#xff0c;然而使用风扇缓解夏日酷热的同时也存在着一些问题。比如&#xff0c;由于风扇的转动方向只能机械式的保持在一定范围内&#xff0c;而不能根据人群的位置做出具体的调整&#xff0c;即在一片区域内&#x…

MongoDB详解

目录 一、MongoDB概述 1.MongoDB定义 2.MongoDB主要特点 2.1文档 2.2集合 2.3数据库 2.4数据模型 二、安装MongoDB 1.Windows安装MongoDB 1.1下载MongoDB 1.2安装MongoDB 1.3配置MongoDB 1.3.1可能遇到的问题 1.4安装一盒可视化工具 2.Linux安装MongoDB 2.1下载…

苍穹外卖项目

Day01 收获 补习git Git学习之路-CSDN博客 nginx 作用&#xff1a;反向代理和负载均衡 swagger Swagger 与 Yapi Swagger&#xff1a; 可以自动的帮助开发人员生成接口文档&#xff0c;并对接口进行测试。 项目接口文档网址&#xff1a; ​​​​​​​http://localhost:808…

Claude聊天机器人推出全新iOS客户端及团队专属计划

Anthropic 正在使其 Claude AI 更易于在移动设备上访问。该公司发布了适用于 iOS 的 Claude 移动应用程序,任何用户都可以免费下载。与聊天机器人的移动网络版本类似,该应用程序跨设备同步用户与 Claude 的对话,允许他们从计算机跳转到应用程序(反之亦然),而不会丢失聊天…

带权并查集

续前章节&#xff1a;并查集及应用 目录 1 带权问题1.1 点带权1.2 边带权 2 例题2.1 家族合并2.2 信息传递2.3 [NOI2002] 银河英雄传说 1 带权问题 1.1 点带权 用num[i]记录节点 i i i 所在的集合的数量。 初始化&#xff1a;所有的num[i]都是 1 1 1&#xff0c;因为每个点…

【stm32-2】按键控制LED光敏传感器控制蜂鸣器

1.按键控制LED uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //读取输入数据寄存器某一个端口的输入值 uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx); //读取整个输入数据寄存器 uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDe…

Linux基础指令001

名称日期版本说明作者了解并熟练运用Linux基础指令2024/05/04v0.0.1汇总篇lgb 一&#xff0c;了解Linux,并安装 Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协…

【机器学习-21】集成学习---Bagging之随机森林(RF)

【机器学习】集成学习---Bagging之随机森林&#xff08;RF&#xff09; 一、引言1. 简要介绍集成学习的概念及其在机器学习领域的重要性。2. 引出随机森林作为Bagging算法的一个典型应用。 二、随机森林原理1. Bagging算法的基本思想2. 随机森林的构造3. 随机森林的工作机制 三…

导弹追踪效果实现_unity基础开发教程

Unity开发中导弹追踪的原理与实现 前言原理逻辑实现导弹逻辑目标赋值 应用效果结语 前言 ⭕在之前的一个项目的开发中&#xff0c;需要加入一个导弹追踪的游戏功能&#xff0c;且还要实现不规则发射路径&#xff0c;但是这种功能是第一次做&#xff0c;经过查阅资料和询问做过的…

Pytorch快速上手

Pytorch快速上手 一、加载数据集 &#xff08;Dataset&#xff09; 加载数据集需要继承Dataset&#xff0c;通常情况下需要实现__init__方法、__getitem__方法以及__len__方法。 案例一&#xff1a; import osimport torch from torch.utils.data import Dataset from PIL …

[嵌入式AI从0开始到入土]17_Ascend C算子开发

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第1期 昇腾Altas 200 DK上手 第2期 下载昇腾案例并运行 第3期 官…

C++静态数组和C语言静态数组的区别( array,int a[])

目录 一、区别 1、越界读&#xff0c;检查不出来 2、越界写&#xff0c;抽查&#xff0c;可能检查不出来&#xff0c;有局限性 二、array缺点 一、区别 C语言的静态数组int a[]; 静态数组的越界检查不稳定的&#xff1a; 1、越界读&#xff0c;检查不出来 2、越界写&#x…

通过helm在k8s上安装minio

1 helm安装minio 1.1 下载minio 添加仓库 helm repo add bitnami https://charts.bitnami.com/bitnami 将minio拉取下来 helm pull bitnami/minio --version 版本号 解压到本地开始编辑配置文件 tar -zxf minio-xxx.tgz [rootk8s-master01 minio]# vi values.yaml 1.2…

Python-VBA函数之旅-open函数

目录 一、open函数的常见应用场景 二、open函数使用注意事项 三、如何用好open函数&#xff1f; 1、open函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;神奇夜光杯-CSDN博客 一、open函数的常见应用场…

Kannala-Brandt 鱼眼相机模型

最近在学习 ORB-SLAM3 的源代码&#xff0c;并模仿、重构了相机模型的实现 在学习的过程中发现针孔相机 (Pinhole) 与鱼眼相机 (Fisheye) 都有畸变参数&#xff0c;但是鱼眼相机无法使用 cv::undistort 函数去畸变 在对鱼眼相机的深度归一化平面进行可视化后&#xff0c;发现…

SQL 注入神器:SQLMap 简单使用

前言 SQLMap 是一款用于自动化 SQL 注入检测与渗透测试的开源工具&#xff0c;其主要功能是检测和利用 Web 应用程序中的 SQL 注入漏洞。以下是 SQLMap 的主要特点和功能&#xff1a; 自动化检测&#xff1a;SQLMap 可以自动发现 Web 应用程序中的 SQL 注入漏洞&#xff0c;包…

QT5之windowswidget_菜单栏+工具栏_核心控件_浮动窗口_模态对话框_标准对话框/文本对话框

菜单栏工具栏 新建工程基类是QMainWindow 1、 2、 3、 点.pro文件&#xff0c;添加配置 因为之后用到lambda&#xff1b; 在.pro文件添加配置c11 CONFIG c11 #不能加分号 添加头文件 #include <QMenuBar>//菜单栏的头文件 主窗口代码mainwindow.cpp文件 #include &q…