当前位置: 首页 > news >正文

吴恩达机器学习笔记(8)—神经网络:反向传播算法(附代码)

目录

  • 一、代价函数
  • 二、反向传播算法
    • 1、数学推导:问题转化
    • 2、数学推导:输出层误差
    • 3、数学推导:隐藏层误差
    • 4、步骤总结
  • 三、参数展开
  • 四、梯度检验
  • 五、参数随机初始化
  • 六、代码实现

上一节介绍了神经网络模型可以利用前向传播做预测,并引入了一系列符号来描述神经网络模型:

  • xix_ixi 表示输入层的第 iii 个输入特征,其中 x0x_0x0 偏置项省略;
  • ai(j)a_i^{(j)}ai(j) 表示第 jjj 层的第 iii 个激活单元,其中 a0(j)a_0^{(j)}a0(j) 偏置单元省略;
  • sjs_jsj 表示第 jjj 层的神经元数量(不包含偏置项),a(j)∈Rsj+1a^{(j)} \in \mathbb{R}^{s_j + 1}a(j)Rsj+1 表示加上偏置项后的 a(j)a^{(j)}a(j)
  • Θ(j)∈Rsj+1×(sj+1)\Theta^{(j)} \in \mathbb{R}^{s_{j + 1} \times (s_j + 1)}Θ(j)Rsj+1×(sj+1) 表示第 jjj 层到第 j+1j + 1j+1 层的权重矩阵Θ\ThetaΘ 表示所有权重矩阵的集合

其中的权重参数,与前面的回归问题类似,需要通过最小化代价函数来逼近。

一、代价函数

为了描述代价函数,我们引入新的符号:

  • LLL 表示神经网络的层数,包含输入层、隐藏层、输出层,因此共有 L−1L - 1L1 次传播;
  • K⩾3K \geqslant 3K3 表示多分类问题的类别数,相应的有 sL=Ks_L = KsL=Khθ(x)∈RKh_\theta(x) \in \mathbb{R}^Khθ(x)RK
  • hθ(x(i))∈RKh_\theta(x^{(i)}) \in \mathbb{R}^Khθ(x(i))RK,表示从第 iii 个样本经过神经网络得到的输出结果,hθ(x(i))kh_\theta(x^{(i)})_khθ(x(i))k 表示其第 kkk 维。

回顾正则化逻辑回归中的交叉熵代价函数
J(θ)=−1m∑i=1m[y(i)log⁡hθ(x(i))+(1−y(i))log⁡(1−hθ(x(i)))]+λ2m∑j=1nθj2J(\theta) = -\frac{1}{m} \sum_{i=1}^{m} \left[ y^{(i)} \log h_\theta(x^{(i)}) + (1 - y^{(i)}) \log (1 - h_\theta(x^{(i)})) \right] + \frac{\lambda}{2m} \sum_{j=1}^{n} \theta_j^2J(θ)=m1i=1m[y(i)loghθ(x(i))+(1y(i))log(1hθ(x(i)))]+2mλj=1nθj2

在神经网络中,我们沿用交叉熵代价函数而不是均方误差,原因如下:

  1. 我们使用了 sigmoid 函数作为激活函数引入非线性,其单增 SSS 形曲线使其在 y=0y = 0y=0111 附近导数较小;
  2. 通常我们会随机初始化参数后进行梯度下降,如果使用均方误差代价函数,对 θ\thetaθ 求偏导后含有 sigmoid 的导数项,会使前期的梯度下降十分缓慢;
  3. 如果使用交叉熵代价函数,则求偏导后不含 sigmoid 的导数项,前期收敛更快。

具体地,对于一个 KKK 分类的神经网络而言,可以看作是 KKK 个二分类的交叉熵代价函数之和
J(Θ)=−1m[∑i=1m∑k=1Kyk(i)ln⁡(hΘ(x(i)))k+(1−yk(i))ln⁡(1−hΘ(x(i)))k]+λ2m∑l=1L−1∑i=1sl∑j=1sl+1(Θji(l))2J(\Theta) = -\frac{1}{m}\left[ \sum_{i=1}^{m} \sum_{k=1}^{K} y_k^{(i)} \ln \left(h_\Theta(x^{(i)})\right)_k + (1 - y_k^{(i)}) \ln \left(1 - h_\Theta(x^{(i)})\right)_k \right] + \frac{\lambda}{2m} \sum_{l=1}^{L-1} \sum_{i=1}^{s_l} \sum_{j=1}^{s_{l+1}} \left(\Theta_{ji}^{(l)}\right)^2J(Θ)=m1[i=1mk=1Kyk(i)ln(hΘ(x(i)))k+(1yk(i))ln(1hΘ(x(i)))k]+2mλl=1L1i=1slj=1sl+1(Θji(l))2

注意这里有一个符号混用的地方:上标 (i)(i)(i)x(i)x^{(i)}x(i)y(i)y^{(i)}y(i) 中表示第 iii 个数据的输入 (∈Rn+1)(\in \mathbb{R}^{n+1})(Rn+1) 和输出 (∈RK)(\in \mathbb{R}^K)(RK) ,但是在 Θ(l)\Theta^{(l)}Θ(l) 中表示第 lll 层的 Θ(∈Rsl+1×(sl+1))\Theta (\in \mathbb{R}^{s_{l+1} \times (s_l + 1)})Θ(Rsl+1×(sl+1))

二、反向传播算法

现在我们的目标就是最小化代价函数 J(Θ)J(\Theta)J(Θ) ,并找到使之最小的参数 Θ\ThetaΘ ,我们仍使用梯度下降法解决最小化问题(尽管 J(Θ)J(\Theta)J(Θ) 可能为非凸函数)。因此需要计算梯度 ∂J∂Θji(l)\frac{\partial J}{\partial \Theta_{ji}^{(l)}}Θji(l)J ,而计算的关键就是「反向传播」:从输出层开始逐层向前计算。

  • 为什么需要「反向传播」?我们知道梯度反映的是 JJJΘji(l)\Theta_{ji}^{(l)}Θji(l) 方向的变化率,即:当参数发生微小增量时,代价函数的增量。对于前几层网络,参数的增量需要经过多层全连接传播,才会影响到输出(代价函数)。
  • 如果我们从输入层开始计算每个参数的梯度,则每个参数都要依赖其后续路径的传播,每个参数的计算都要先算一遍后续路径的梯度。而如果从输出层开始逐层向前计算并存储了梯度,则解耦了依赖关系,前一层可以利用后一层的计算结果。有点类似「动态规划」中「递归求解子问题」的思想。

1、数学推导:问题转化

为了推导方便,先假设只有一组数据 (x,y)(x, y)(x,y) ,这样可以省去上标和求和的麻烦。并且令 λ=0\lambda = 0λ=0 省略正则化项,等到最后的偏导结果再加上。

我们首先对第 l+1l + 1l+1 层的第 jjj 个神经元(偏置单元除外)定义一个 delta 误差:
δj(l+1)=∂J∂zj(l+1)\delta_j^{(l + 1)} = \frac{\partial J}{\partial z_j^{(l + 1)}}δj(l+1)=zj(l+1)J

其中 zj(l+1)=∑k=0slΘjk(l)ak(l)z_j^{(l + 1)} = \sum_{k = 0}^{s_l} \Theta_{jk}^{(l)} a_k^{(l)}zj(l+1)=k=0slΘjk(l)ak(l) ,即 aj(l+1)a_j^{(l + 1)}aj(l+1) 神经元取 sigmoid 激活前的输出值。换句话说,aj(l+1)=g(zj(l+1))a_j^{(l + 1)} = g\left(z_j^{(l + 1)}\right)aj(l+1)=g(zj(l+1))

于是根据链式求导法则,我们有:
∂J∂Θji(l)=∂J∂zj(l+1)⋅∂zj(l+1)∂Θji(l)=δj(l+1)ai(l)\frac{\partial J}{\partial \Theta_{ji}^{(l)}} = \frac{\partial J}{\partial z_j^{(l + 1)}} \cdot \frac{\partial z_j^{(l + 1)}}{\partial \Theta_{ji}^{(l)}} = \delta_j^{(l + 1)} a_i^{(l)}Θji(l)J=zj(l+1)JΘji(l)zj(l+1)=δj(l+1)ai(l)

写作矩阵形式:
Δ(l)=δ(l+1)⋅(a(l))T\Delta^{(l)} = \delta^{(l + 1)} \cdot \left(a^{(l)}\right)^TΔ(l)=δ(l+1)(a(l))T

显然,一旦求出当前层的 deltadeltadelta 误差,那么传入当前层的各参数的梯度也可求出。所以现在问题转化为求解 δ(l+1)\delta^{(l + 1)}δ(l+1)

2、数学推导:输出层误差

既然要递归求解子问题,首先就是要算输出层,即 δj(L)\delta_j^{(L)}δj(L)
δj(L)=∂J∂zj(L)=∂J∂aj(L)⋅∂aj(L)∂zj(L)=∂J∂aj(L)⋅g′(zj(L))=−(yjaj(L)−1−yj1−aj(L))⋅(aj(L)(1−aj(L)))=aj(L)−yj\begin{aligned} \delta_j^{(L)} &= \frac{\partial J}{\partial z_j^{(L)}} = \frac{\partial J}{\partial a_j^{(L)}} \cdot \frac{\partial a_j^{(L)}}{\partial z_j^{(L)}} \\ &= \frac{\partial J}{\partial a_j^{(L)}} \cdot g'\left(z_j^{(L)}\right) \\ &= -\left( \frac{y_j}{a_j^{(L)}} - \frac{1 - y_j}{1 - a_j^{(L)}} \right) \cdot \left( a_j^{(L)} \left( 1 - a_j^{(L)} \right) \right) \\ &= a_j^{(L)} - y_j \end{aligned}δj(L)=zj(L)J=aj(L)Jzj(L)aj(L)=aj(L)Jg(zj(L))=(aj(L)yj1aj(L)1yj)(aj(L)(1aj(L)))=aj(L)yj

写作矩阵形式:
δ(L)=a(L)−y\delta^{(L)} = a^{(L)} - yδ(L)=a(L)y

  • 此处推导过程的一些注释:
  • 关于第二行,请注意:a(L)=g(z(L))a^{(L)} = g(z^{(L)})a(L)=g(z(L))
  • 关于第三行,请注意 sigmoid 函数的性质:g′(z)=g(z)(1−g(z))g'(z) = g(z)(1 - g(z))g(z)=g(z)(1g(z))
  • 以及第三行偏导项的计算,请注意 a(L)=hΘ(x)a^{(L)} = h_\Theta(x)a(L)=hΘ(x) ,所以 J(Θ)J(\Theta)J(Θ) 在现在的假设条件下可以写作:
    J(Θ)=−[∑k=1Kykln⁡(ak(L))+(1−yk)ln⁡(1−ak(L))]J(\Theta) = -\left[ \sum_{k = 1}^{K} y_k \ln\left(a_k^{(L)}\right) + (1 - y_k)\ln\left(1 - a_k^{(L)}\right) \right]J(Θ)=[k=1Kykln(ak(L))+(1yk)ln(1ak(L))]

3、数学推导:隐藏层误差

下面计算第 lll(2⩽l<L)(2 \leqslant l < L)(2l<L)δj(l)\delta_j^{(l)}δj(l)
δj(l)=∂J∂zj(l)=∂J∂zj(l+1)⋅∂zj(l+1)∂aj(l)⋅∂aj(l)∂zj(l)=δ(l+1)T⋅Θj(l)⋅g′(zj(l))=δ(l+1)T⋅Θj(l)⋅(aj(l)(1−aj(l)))\begin{aligned} \delta_j^{(l)} &= \frac{\partial J}{\partial z_j^{(l)}} \\ &= \frac{\partial J}{\partial z_j^{(l + 1)}} \cdot \frac{\partial z_j^{(l + 1)}}{\partial a_j^{(l)}} \cdot \frac{\partial a_j^{(l)}}{\partial z_j^{(l)}} \\ &= \delta^{(l + 1)T} \cdot \Theta_{j}^{(l)} \cdot g'\left(z_j^{(l)}\right) \\ &= \delta^{(l + 1)T} \cdot \Theta_{j}^{(l)} \cdot \left(a_j^{(l)} \left(1 - a_j^{(l)}\right)\right) \end{aligned}δj(l)=zj(l)J=zj(l+1)Jaj(l)zj(l+1)zj(l)aj(l)=δ(l+1)TΘj(l)g(zj(l))=δ(l+1)TΘj(l)(aj(l)(1aj(l)))

观察式子的前半部分,可以看出下一层所有结点的 deltadeltadelta 误差均会影响当前层的任一结点,而影响则是通过该结点链出的权重参数反向传播。

转置后,写作矩阵形式:
δ(l)=(Θ(l)Tδ(l+1))⊙(a(l)⊙(1−a(l)))\delta^{(l)} = \left( \Theta^{(l)T} \delta^{(l + 1)} \right) \odot \left( a^{(l)} \odot \left( 1 - a^{(l)} \right) \right)δ(l)=(Θ(l)Tδ(l+1))(a(l)(1a(l)))

其中 ⊙\odot 表示 Hadamard 积,即两个向量对应位置相乘。

4、步骤总结

以上是对一组数据的推导,我们得到了三个重要的结果 (1)(2)(3):
{Δ(l)=δ(l+1)⋅(a(l))T1⩽l<Lδ(L)=a(L)−yδ(l)=(Θ(l)Tδ(l+1))⊙(a(l)⊙(1−a(l)))2⩽l<L\begin{cases} \Delta^{(l)} = \delta^{(l + 1)} \cdot \left( a^{(l)} \right)^T & 1 \leqslant l < L \\ \delta^{(L)} = a^{(L)} - y \\ \delta^{(l)} = \left( \Theta^{(l)T} \delta^{(l + 1)} \right) \odot \left( a^{(l)} \odot \left( 1 - a^{(l)} \right) \right) & 2 \leqslant l < L \end{cases}Δ(l)=δ(l+1)(a(l))Tδ(L)=a(L)yδ(l)=(Θ(l)Tδ(l+1))(a(l)(1a(l)))1l<L2l<L

mmm 组数据只需要在一些地方进行累加即可。设数据集为 {(x(i),y(i))∣1⩽i⩽m}\{ (x^{(i)}, y^{(i)}) \mid 1 \leqslant i \leqslant m \}{(x(i),y(i))1im} ,则反向传播算法的步骤如下:

  1. 所有 Δ(l)\Delta^{(l)}Δ(l) 置零;

  2. 遍历数据集,设当前数据为 (x(i),y(i))(x^{(i)}, y^{(i)})(x(i),y(i))
    i. 以 x(i)x^{(i)}x(i) 为输入做前向传播,得到输出 a(L)a^{(L)}a(L)
    ii. 公式 (2) 计算输出层误差,公式 (3) 计算隐藏层误差;
    iii. 公式 (1) 更新各层的 Δ(l)\Delta^{(l)}Δ(l) 矩阵,进行累加Δ(l):=Δ(l)+δ(l+1)⋅(a(l))T\Delta^{(l)} := \Delta^{(l)} + \delta^{(l + 1)} \cdot \left( a^{(l)} \right)^TΔ(l):=Δ(l)+δ(l+1)(a(l))T

  3. 计算 DDD 矩阵,对偏置项以外的参数进行正则化
    Dij(l):={1m(Δij(l)+λΘij(l))if j≠01mΔij(l)if j=0D_{ij}^{(l)} := \begin{cases} \frac{1}{m} \left( \Delta_{ij}^{(l)} + \lambda \Theta_{ij}^{(l)} \right) & \text{if } j \neq 0 \\ \frac{1}{m} \Delta_{ij}^{(l)} & \text{if } j = 0 \end{cases}Dij(l):={m1(Δij(l)+λΘij(l))m1Δij(l)if j=0if j=0

这就是最终的梯度矩阵:∂J∂Θij(l)=Dij(l)\frac{\partial J}{\partial \Theta_{ij}^{(l)}} = D_{ij}^{(l)}Θij(l)J=Dij(l)

现在,我们可以用 Dij(l)D_{ij}^{(l)}Dij(l) 做一次梯度下降了,整个步骤称为一代(epoch)。注意,我们的参数 Θ(l)\Theta^{(l)}Θ(l) 应该初始化为 [−ϵ,ϵ][-\epsilon, \epsilon][ϵ,ϵ] 中的随机值

三、参数展开

实现过程中,由于每一层都有一个 Θ(l)\Theta^{(l)}Θ(l)D(l)D^{(l)}D(l) 矩阵,总共有 2×L2 \times L2×L 个矩阵,为了方便存储和计算,我们最好都转换为向量,并将各层的向量拼接成一个长向量。这样做可以方便调用封装好的高级优化函数接口,如 fminuncscipy.optimize.minimize

同样,函数输出的结果向量也可以通过 reshape 转为矩阵,再应用于前向传播。

四、梯度检验

当我们为一个较为复杂的模型(例如神经网络)实现梯度下降算法时,可能会出现一些不易察觉的错误,导致虽然代价看上去在不断减小,但最终的结果可能并不是最优解,误差可能高出一个量级。

为了避免这样的问题,可采取梯度的数值检验方法:通过数值计算得到梯度的近似值,再和反向传播的结果比对,检验是否接近。

在任意一代计算后,得到一组 θ\thetaθ 向量及 DDD 矩阵,通过差商近似 J(θ)J(\theta)J(θ) 的各偏导:
∂∂θkJ(θ)≈J(θ1,⋯,θk+ϵ,⋯,θn)−J(θ1,⋯,θk−ϵ,⋯,θn)2ϵ\frac{\partial}{\partial \theta_k} J(\theta) \approx \frac{J(\theta_1, \cdots, \theta_k + \epsilon, \cdots, \theta_n) - J(\theta_1, \cdots, \theta_k - \epsilon, \cdots, \theta_n)}{2\epsilon}θkJ(θ)2ϵJ(θ1,,θk+ϵ,,θn)J(θ1,,θkϵ,,θn)

其中,ϵ\epsilonϵ 是非常小的常数,为了避免计算误差,通常选取 10−410^{-4}104。现在我们可以比较这些偏导估计值与对应位置 Dij(l)D_{ij}^{(l)}Dij(l) 的值,它们应该非常接近。

注意:梯度检验耗时巨大,复杂度远大于反向传播,一旦验证了神经网络反向传播的代码正确后,不应进行梯度检验(删掉 or 注释掉)。

五、参数随机初始化

前文我们提到参数 Θ(l)\Theta^{(l)}Θ(l) 应该初始化为 [−ϵ,ϵ][-\epsilon, \epsilon][ϵ,ϵ] 中的随机值,而不是简单初始化为全零。如果将 Θ(l)\Theta^{(l)}Θ(l) 初始化为全零,则下一层的 z(l+1)z^{(l + 1)}z(l+1) 也为全零,则 a(l+1)a^{(l + 1)}a(l+1) 就全为 0.50.50.5,且所有隐藏层都会得到这个结果;与此同时,反向传播公式 (3) 中,由输出层到隐藏层时 deltadeltadelta 误差也会清零。这将导致网络无法传播!

那么,如果将参数 Θ(l)\Theta^{(l)}Θ(l) 全都初始化为同一个非零常量呢?由于全连接,同一层的 a(l+1)a^{(l + 1)}a(l+1) 将为相同值;在反向传播公式 (3) 中,由输出层到隐藏层时 deltadeltadelta 误差也会变成相同值,则所有的 D(l)D^{(l)}D(l) 矩阵也为常数矩阵,参数 Θ(l)\Theta^{(l)}Θ(l) 将再次被更新为新的相同常量。这样下去所有的激活单元都成了摆设,因为每一层都在重复计算着相同特征。

  1. 上述问题也被称为对称权重问题,解决方法就是随机初始化,实现时可以将 [0,1][0, 1][0,1] 的随机数映射到 [−ϵ,ϵ][-\epsilon, \epsilon][ϵ,ϵ] ,这里的 ϵ\epsilonϵ 与梯度检验中的无关

  2. 实际应用中 ϵ\epsilonϵ 的选取,通常与 Θ(l)\Theta^{(l)}Θ(l) 前后两层的神经元个数有关,一种常用的有效的取值是:
    ϵ(l)=6s(l)+s(l+1)\epsilon^{(l)} = \frac{\sqrt{6}}{\sqrt{s^{(l)} + s^{(l + 1)}}}ϵ(l)=s(l)+s(l+1)6

六、代码实现

下面以 Coursera 上的多分类数据集 ex4data1.mat 为例,这是一个手写数字的数据集,与上一节的数据集类似。共 5000 组数据,每组数据输入是一个由 20×2020 \times 2020×20 灰度矩阵压缩而来的 400 维向量,输出是 0 到 9 之间的整数。

题目还提供一组训练好的权重参数 ex4weight.mat,用以检验前向传播和代价函数的正确性,此处跳过。采用题目推荐的网络层数 L=3L = 3L=3,则输入层有 401 个单元(含偏置项),隐藏层有 26 个单元(含偏置项),输出层有 10 个单元(独热编码)。

先进行预处理,包括数据预处理、随机初始化参数:

import numpy as np
import scipy.io as scio
from sklearn.metrics import classification_report# load data
data = scio.loadmat('ex4data1.mat')
X = data['X'] 			# (5000, 400), 这里不转置
y = data['y'].flatten()	# (5000, )
y[y==10] = 0# parameter
(m, n) = (5000, 401)
L = 3 				 # layer of network
s = [0, 400, 25, 10] # size of each layer
lmd = 1				 # for regularization
alpha = 0.1
num_iters = 5000
#J_history = []# One-hot encoder
Y = np.zeros((m, s[L]))
for i in range(m):Y[i][y[i]] = 1# random init Theta, (25, 401) (10, 26)
Theta = [None] * L
for l in range(1, L):Theta[l] = np.random.rand(s[l+1], s[l]+1)eps = np.sqrt(6) / np.sqrt(s[l+1] + s[l])Theta[l] = Theta[l] * 2 * eps - eps

再实现前向传播与代价函数计算,代价函数这里用于绘图、梯度检验:

def sigmoid(z):return 1 / (1 + np.exp(-z))def forwardProp():a = [None] * (L+1)# input layera[1] = np.c_[np.ones(m), X] 				# (5000, 401)# hidden layerfor l in range(2, L):a[l] = sigmoid(a[l-1] @ Theta[l-1].T)	# (5000, 25)a[l] = np.c_[np.ones(m), a[l]]			# (5000, 26)# output layera[L] = sigmoid(a[L-1] @ Theta[L-1].T)		# (5000, 10)return adef J(a_L):'''a_L : output layer animation, (m, s[L])'''res = - (1 / m) * np.sum(Y * np.log(a_L) + (1-Y) * np.log(1-a_L))for l in range(1, L):res += lmd / (2 * m) * np.sum(np.power(Theta[l][:, 1:], 2))return res

实现反向传播算法,注意这里偏置单元deltadeltadelta 误差没有意义,算出的值要忽略:

def backProp():# init Delta -> 0, should be (s_{l+1}, s_l + 1)Delta = [None] * Lfor l in range(1, L):Delta[l] = np.zeros((s[l+1], s[l]+1))# init delta, should be (s_l, )delta = [None] * (L+1)# step 1a = forwardProp()#J_history.append(J(a[L]))# step 2for i in range(m):delta[L] = a[L][i, :] - Y[i, :]  # 降维成 (s_L, )for l in range(L-1, 1, -1):delta[l] = ((Theta[l].T @ delta[l+1]) * (a[l][i, :] * (1 - a[l][i, :])))[1:]for l in range(1, L):Delta[l] += delta[l+1][:,np.newaxis] @ a[l][i:i+1, :]  # 升维成 (s_l, 1)# step 3D = [None] * L  # init D, same as Deltafor l in range(1, L):D[l] = (1 / m) * (Delta[l] + lmd * np.c_[np.zeros(s[l+1]), Theta[l][:, 1:]])return D

实现梯度下降算法并检验结果,大约耗时 20 分钟:

# Gradient Descent
for i in range(0, num_iters):print(i)D = backProp()for l in range(1, L):Theta[l] -= alpha * D[l]# Prediction,调库生成混淆矩阵
y_pred = np.argmax(forwardProp()[L], axis=1)	# (5000, )
print(classification_report(y, y_pred, digits=3))

如果要进行梯度检验,只需要将下述函数插入到 D = backProp() 后即可,正式训练时删去:

# Gradient Check
def gradCheck():for l in range(1, L):for i in range(s[l+1]):for j in range(s[l]+1):Theta[l][i, j] -= 0.0001J1 = J(forwardProp()[L])Theta[l][i, j] += 0.0002J2 = J(forwardProp()[L])Theta[l][i, j] -= 0.0001print(D[l][i, j], (J2 - J1) / 0.0002)

5000 轮次后,最终的预测结果如下:

              precision    recall  f1-score   support0      0.950     0.984     0.967       5001      0.946     0.978     0.962       5002      0.944     0.914     0.929       5003      0.921     0.914     0.918       5004      0.948     0.940     0.944       5005      0.919     0.904     0.911       5006      0.949     0.964     0.956       5007      0.950     0.942     0.946       5008      0.935     0.924     0.930       5009      0.926     0.924     0.925       500accuracy                          0.939      5000macro avg      0.939     0.939     0.939      5000
weighted avg      0.939     0.939     0.939      5000

文章转载自:

http://ZNM3bZHO.kmwsz.cn
http://7ImpeTIb.kmwsz.cn
http://EhVcpnCC.kmwsz.cn
http://cnFLYtJF.kmwsz.cn
http://pZrup9RT.kmwsz.cn
http://tfT7G5XV.kmwsz.cn
http://UxNafou6.kmwsz.cn
http://h3q397Cr.kmwsz.cn
http://2eZIdjWs.kmwsz.cn
http://JercuddW.kmwsz.cn
http://pE3CSsy3.kmwsz.cn
http://hlOX7QE8.kmwsz.cn
http://iOkwl0ob.kmwsz.cn
http://W1FfZZeY.kmwsz.cn
http://3b2W1efr.kmwsz.cn
http://NzXP28mJ.kmwsz.cn
http://BlciKZk1.kmwsz.cn
http://6BElqhE5.kmwsz.cn
http://wFHIOZUy.kmwsz.cn
http://fduTQZ4e.kmwsz.cn
http://C50mNhDg.kmwsz.cn
http://RMKPDDVW.kmwsz.cn
http://YWaeJzL8.kmwsz.cn
http://B3Jku3d9.kmwsz.cn
http://ODwQKBqb.kmwsz.cn
http://vqpwtaje.kmwsz.cn
http://Wwv87ltA.kmwsz.cn
http://KAyPpEFy.kmwsz.cn
http://KtcokUdK.kmwsz.cn
http://2qeYwwZS.kmwsz.cn
http://www.dtcms.com/a/377580.html

相关文章:

  • 仓颉安装文档
  • Product Hunt 每日热榜 | 2025-09-09
  • 大数据毕业设计-大数据-基于大数据的热门游戏推荐与可视化系统(高分计算机毕业设计选题·定制开发·真正大数据)
  • 解读数据仓库知识培训【附全文阅读】
  • LangChain中的Prompt模板如何使用?
  • 防逆流·易安装·高兼容——安科瑞ADL200N-CT/D16-WF阳台光伏电表
  • 性能优化零成本:只加3行代码,FCP从1.8s砍到1.2s
  • 深入 Spring MVC 底层:控制器方法执行流程与参数绑定原理解析
  • UniApp微信小程序-实现蓝牙功能
  • Java集成SmartJavaAI实现旋转框检测、定向边界框目标检测(YOLO-OBB)
  • FreeBSD系统使用freebsd-update命令从14.2升级到14.3
  • 【Java】Hibernate查询性能优化
  • Spring DI/IOC核心原理详解
  • 基于多时间尺度的电动汽车光伏充电站联合分层优化调度(Matlab代码实现)
  • 【论文阅读】TrojVLM: Backdoor Attack Against Vision Language Models
  • 快速查看文件的MD5码
  • 多模态大模型研究每日简报【2025-09-10】
  • 股指期货合约的代码如何理解?
  • 基于Python的商品爬取与可视化系统
  • SEGGER_RTT相关的操作
  • vmware虚拟机 ubuntu固定usb转rndis网卡
  • Java管理事务方式
  • Spring Boot + Vue 项目中使用 Redis 分布式锁案例
  • Unity(①基础)
  • 【测量】知识点
  • 开始 ComfyUI 的 AI 绘图之旅-ControlNet(六)
  • 楼宇自控系统监控建筑变配电系统:功效体现在安全与节能层面
  • 分布式存储:RustFS与MinIO全面对比
  • 【第24话:定位建图】 SLAM回环检测方法及原理详细介绍
  • Electron 核心模块速查表