AutoCV第十一课:DL基础

news2024/9/30 17:38:41

目录

  • DL基础
    • 前言
    • 1. BP训练mnist
    • 2. 权重初始化理论分析
    • 总结

DL基础

前言

手写AI推出的全新保姆级从零手写自动驾驶CV课程,链接。记录下个人学习笔记,仅供自己参考。

本次课程我们来了解下 BP 反向传播和学习权重初始化相关知识

课程大纲可看下面的思维导图。

在这里插入图片描述

1. BP训练mnist

BP(back propagation)误差反向传播算法是当前人工智能主要采用的算法,如 CNN、GAN、Transformer 等都是 BP 体系下的算法框架,这节课我们从一个不同的角度来了解下 BP

我们先来回顾下之前学习到的知识

对于给定房屋面积预测房价的任务,属于单个变量和单个输出的模型,其对应表达如下:
y = k x + b y = kx + b y=kx+b
对于给定房屋面积、距离地铁远近、装修程度预测房价的任务,属于多个变量和单个输出的模型,其对应表达如下:
y = k 1 x 1 + k 2 x 2 + k 3 x 3 + b y = k_1x_1+k_2x_2+k_3x_3+b y=k1x1+k2x2+k3x3+b
对于给定房屋面积、距离地铁远近、装修程度预测房价和租金的任务,属于多个变量和多个输出的模型(多模型),其对应表达如下:
y 1 = k 1 x 1 + k 2 x 2 + k 3 x 3 + b 1 y 2 = p 1 x 1 + p 2 x 2 + p 3 x 3 + b 2 y_1 = k_1x_1 + k_2x_2 + k_3x_3 + b_1 \\ y_2 = p_1x_1 + p_2x_2 + p_3x_3 + b_2 y1=k1x1+k2x2+k3x3+b1y2=p1x1+p2x2+p3x3+b2
而我们今天要学习的 BP 可以看作是多模型的堆叠,将中间的表示抽象出来,我们以一次抽象为例来讲解,实际可以进行多次抽象,看作是多个层次的堆叠

中间的抽象层可以定义为:
h 1 = k 1 x 1 + k 2 x 2 + k 3 x 3 + b 1 h 2 = p 1 x 1 + p 2 x 2 + p 3 x 3 + b 2 h_1 = k_1x_1 + k_2x_2 + k_3x_3 + b_1 \\ h_2 = p_1x_1 + p_2x_2 + p_3x_3 + b_2 h1=k1x1+k2x2+k3x3+b1h2=p1x1+p2x2+p3x3+b2
我们往往不只是做简单的堆叠,因为这样堆叠出来的模型没有非线性的特性,表达能力偏弱,因此,我们会在中间层之后额外加一个非线性的表达,增强模型的表达能力。
h 1 = f u n c ( h 1 ) h 2 = f u n c ( h 2 ) h_1 = func(h_1) \\ h_2 = func(h_2) h1=func(h1)h2=func(h2)
最后我们再得到我们想要的目标结果
y 1 = m 1 h 1 + m 2 h 2 + b 3 y 2 = n 1 h 1 + n 2 h 2 + b 4 \begin{aligned} y_1 &= m_1h_1 + m_2h_2 + b3 \\ y_2 &= n_1h_1 + n_2h_2 + b4 \end{aligned} y1y2=m1h1+m2h2+b3=n1h1+n2h2+b4
OK!经过上述分析之后,我们再来看具体的代码实现

我们只需要将上节课中的多分类逻辑回归的示例代码略微修改即可

我们需要加一层隐藏层,假设隐藏层的输出数量为 256, k k k b b b 的定义如下:

# 定义k和b
hidden_size = 256
num_classes = 10

k1 = np.random.randn(784, hidden_size)
b1 = np.zeros((1, hidden_size))
k2 = np.random.randn(hidden_size, num_classes)
b2 = np.zeros((1, num_classes))

模型预测时也需要发生一些变化,输入先经过 k 1 k_1 k1 b 1 b_1 b1 得到隐藏层的输出,随后经过非线性激活函数 relu,最后经过 k 2 k_2 k2 b 2 b_2 b2 得到最终的预测输出。由于多了一层,因此 loss 的求导也发生了对应的变化,但是原理还是一样,示例代码如下:

def relu(x):
    x = x.copy()
    x[x < 0] = 0
    return x

def drelu(x, G):
    G = G.copy()
    G[x < 0] = 0
    return G

for epoch in range(10):
    for images, labels in train_loader:
        niter += 1
        
        # 32x784 @ 784x256
        hidden = images @ k1 + b1
        
        # nonlinear
        nonlinear_hidden = relu(hidden)

        # predict
        predict = nonlinear_hidden @ k2 + b2

        # predict -> logits
        logits = softmax(predict, dim=1)

        # softmax crossentropy loss
        # loss = -(y * ln(p))
        batch = logits.shape[0]
        onehot_labels = np.zeros_like(logits)

        # labels(32,) -> onehot(32,10)
        onehot_labels[np.arange(batch), labels] = 1
        
        loss = crossentropy_loss(logits, onehot_labels)

        if niter % 100 == 0:
            print(f"Epoch: {epoch}, Iter: {niter}, Loss: {loss:.3f}")

        G = (logits - onehot_labels) / batch
        
        # C = AB
        # dA = G @ B.T
        # dB = A.T @ G
        delta_k2 = nonlinear_hidden.T @ G
        delta_b2 = G.sum(axis=0, keepdims=True)

        delta_nonlinear_hidden = G @ k2.T
        delta_hidden = drelu(hidden, delta_nonlinear_hidden)

        delta_k1 = images.T @ delta_hidden
        delta_b1 = delta_hidden.sum(axis=0, keepdims=True)

        k1 = k1 - lr * delta_k1
        b1 = b1 - lr * delta_b1
        k2 = k2 - lr * delta_k2
        b2 = b2 - lr * delta_b2

验证部分也需要相应的修改,代码如下:

# evaluate
all_predict = []
for images, labels in test_loader:

    hidden = images @ k1 + b1

    # nonlinear
    nonlinear_hidden = relu(hidden)

    predict = nonlinear_hidden @ k2 + b2

    logits = softmax(predict, dim=1)

    predict_labels = logits.argmax(axis=1)
    all_predict.extend(predict_labels == labels)

    accuracy = np.sum(all_predict) / len(all_predict) * 100
    print(f"Epoch: {epoch}, Evaluate Test Set, Accuracy: {accuracy:.3f} %")

运行效果如下:

在这里插入图片描述

图1-1 BP输出1

可以看到 loss 直接飞了,这是因为 k k k 采用正态分布的初始化,其值比较大,当计算 loss 时 ln 直接跑飞了,因此,我们可以将 k 1 k_1 k1 k 2 k_2 k2 缩小,在下次课程中我们会对权重的初始化进行详细的分析,具体代码如下:

k1 = np.random.randn(784, hidden_size) * 0.1
b1 = np.zeros((1, hidden_size))
k2 = np.random.randn(hidden_size, num_classes) * 0.1
b2 = np.zeros((1, num_classes))

修改后运行的效果如下:

在这里插入图片描述

图1-2 BP输出2

可以看到此时的 loss 输出值正常,且第一个 epoch 准确率就达到了 92.93%,效果还是不错的。

接下来我们可以将模型预测的结果进行简单的可视化,代码如下:

for images, labels in test_loader:
    
    hidden = images @ k1 + b1

    nonlinear_hidden = relu(hidden)

    predict = nonlinear_hidden @ k2 + b2

    logits = softmax(predict, dim=1)

    predict_labels = logits.argmax(axis=1)

    pixels = (images * train_dataset.std + train_dataset.mean).astype(np.uint8).reshape(-1, 28, 28)

    for image, predict, gt in zip(pixels, predict_labels, labels):

        plt.imshow(image)
        plt.title(f"Predict: {predict}, GT: {gt}")
        plt.show()

可视化的部分结果如下所示:

在这里插入图片描述

图1-3 预测结果1

在这里插入图片描述

图1-4 预测结果2

可以看到模型预测的结果还是比较准确的。

对于 BP 反向传播算法的不同理解可以参考 彻底理解BP反向传播,BP实战图像分类98.3%

完整的示例代码如下所示:

import numpy as np
import matplotlib.pyplot as plt

class MNISTDataset:
    def __init__(self, images_path, labels_path, train, mean=None, std=None):
        self.images_path = images_path
        self.labels_path = labels_path
        self.images, self.labels = self.load_mnist_data()

        # flatten Nx28x28 -> Nx784
        self.images =  self.images.reshape(len(self.images), -1)
        
        if train:
            self.images, self.mean, self.std = self.normalize(self.images)
        else:
            self.images, self.mean, self.std = self.normalize(self.images, mean, std)

    @staticmethod
    def normalize(x, mean=None, std=None):
        if mean is None:
            mean = x.mean()
        
        if std is None:
            std = x.std()
        
        x = (x - mean) / std
        return x, mean, std

    def __len__(self):
        return len(self.images)

    def __getitem__(self, index):
        image, label = self.images[index], self.labels[index]
        return image, label

    def load_mnist_data(self):
        # load labels
        with open(self.labels_path, "rb") as f:
            magic_number, num_of_items = np.frombuffer(f.read(8), dtype=">i", count=2, offset=0)
            labels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)

        # load images
        with open(self.images_path, "rb") as f:
            magic_number, num_of_images, rows, cols = np.frombuffer(f.read(16), dtype=">i", count=4, offset=0)
            pixels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)
            images_matrix = pixels.reshape(num_of_images, rows, cols)

        return images_matrix, labels

class MNISTDataLoader:
    def __init__(self, dataset, batch_size, shuffle=True):
        self.dataset = dataset
        self.batch_size = batch_size
        self.shuffle = shuffle
        
    def __iter__(self):
        self.indexs = np.arange(len(self.dataset))
        if self.shuffle:
            np.random.shuffle(self.indexs)
        self.cursor = 0
        return self
    
    def __next__(self):
        begin = self.cursor
        end   = self.cursor + self.batch_size
        if end > len(self.dataset):
            raise StopIteration()
        self.cursor = end
        batched_data = []
        for index in self.indexs[begin:end]:
            item = self.dataset[index]
            batched_data.append(item)
        # return batched_data
        return [np.stack(item, axis=0) for item in list(zip(*batched_data))]      

# 训练集
train_dataset = MNISTDataset("mnist/train-images-idx3-ubyte", "mnist/train-labels-idx1-ubyte", train=True)
train_loader  = MNISTDataLoader(train_dataset, batch_size=32, shuffle=True)

# 测试集
test_dataset = MNISTDataset(
    "mnist/t10k-images-idx3-ubyte", 
    "mnist/t10k-labels-idx1-ubyte",
    train=False,
    mean=train_dataset.mean,
    std=train_dataset.std
)
test_loader = MNISTDataLoader(test_dataset, 10)

# 定义k和b
hidden_size = 256
num_classes = 10

k1 = np.random.randn(784, hidden_size) * 0.1
b1 = np.zeros((1, hidden_size))
k2 = np.random.randn(hidden_size, num_classes) * 0.1
b2 = np.zeros((1, num_classes))

def relu(x):
    x = x.copy()
    x[x < 0] = 0
    return x

def drelu(x, G):
    G = G.copy()
    G[x < 0] = 0
    return G

def softmax(x, dim):
    x = np.exp(x - x.max())
    return x / x.sum(axis=dim, keepdims=True)

def crossentropy_loss(logits, onehot_labels):
    batch = logits.shape[0]
    return -(onehot_labels * np.log(logits)).sum() / batch

lr = 1e-2
niter = 0

for epoch in range(1):
    for images, labels in train_loader:
        niter += 1
        
        # 32x784 @ 784x256
        hidden = images @ k1 + b1
        
        # nonlinear
        nonlinear_hidden = relu(hidden)

        # predict
        predict = nonlinear_hidden @ k2 + b2

        # predict -> logits
        logits = softmax(predict, dim=1)

        # softmax crossentropy loss
        # loss = -(y * ln(p))
        batch = logits.shape[0]
        onehot_labels = np.zeros_like(logits)

        # labels(32,) -> onehot(32,10)
        onehot_labels[np.arange(batch), labels] = 1
        
        loss = crossentropy_loss(logits, onehot_labels)

        if niter % 100 == 0:
            print(f"Epoch: {epoch}, Iter: {niter}, Loss: {loss:.3f}")

        G = (logits - onehot_labels) / batch
        
        # C = AB
        # dA = G @ B.T
        # dB = A.T @ G
        delta_k2 = nonlinear_hidden.T @ G
        delta_b2 = G.sum(axis=0, keepdims=True)

        # dloss / dhidden
        delta_nonlinear_hidden = G @ k2.T
        delta_hidden = drelu(hidden, delta_nonlinear_hidden)

        delta_k1 = images.T @ delta_hidden
        delta_b1 = delta_hidden.sum(axis=0, keepdims=True)

        k1 = k1 - lr * delta_k1
        b1 = b1 - lr * delta_b1
        k2 = k2 - lr * delta_k2
        b2 = b2 - lr * delta_b2

    # evaluate
    all_predict = []
    for images, labels in test_loader:
        
        hidden = images @ k1 + b1

        # nonlinear
        nonlinear_hidden = relu(hidden)

        predict = nonlinear_hidden @ k2 + b2

        logits = softmax(predict, dim=1)

        predict_labels = logits.argmax(axis=1)
        all_predict.extend(predict_labels == labels)
    
    accuracy = np.sum(all_predict) / len(all_predict) * 100
    print(f"Epoch: {epoch}, Evaluate Test Set, Accuracy: {accuracy:.3f} %")

for images, labels in test_loader:
    
    hidden = images @ k1 + b1

    nonlinear_hidden = relu(hidden)

    predict = nonlinear_hidden @ k2 + b2

    logits = softmax(predict, dim=1)

    predict_labels = logits.argmax(axis=1)

    pixels = (images * train_dataset.std + train_dataset.mean).astype(np.uint8).reshape(-1, 28, 28)

    for image, predict, gt in zip(pixels, predict_labels, labels):

        plt.imshow(image)
        plt.title(f"Predict: {predict}, GT: {gt}")
        plt.show()

2. 权重初始化理论分析

我们先来对上节课 BP 训练 mnist 做一个回顾,在上节课中我们发现直接采用正态分布的随机数来初始权重 k k k 会导致最终的 loss 直接跑飞,因此我们将其值乘了 0.1 进行缩小,发现缩小后的初始化权重 k k k 能达到一个比较好的结果。

为什么一定要使用正态分布的随机数来对权重进行初始化呢?使用其他分布可以吗?🤔

下面我们对比了采用均匀分布随机数和正态分布随机数来初始化权重 k k k 的损失和模型的性能

rand  -> loss: 2.127, accuracy: 87.010%
randn -> loss: 0.365, accuracy: 93.100%

可以看到二者差别还是比较大的,均匀分布的随机数初始化的 loss 要高,且准确率也偏低。这说明不同的权重初始化方法会影响最终的模型性能,为什么正态分布的随机数初始化会比均匀分布的随机数初始化效果要好呢?这就是我们接下来要讨论的问题。

我们可以回想下之前求取 2 \sqrt 2 2 的例子中,我们将 t t t 初始化为 x / 2 x/2 x/2,因为这个数更能够接近目标值一些,所以它的开始 loss 会更低,迭代次数也会更少。

现在考虑线性回归模型
y = k x + b y = kx + b y=kx+b
问题:如何定义 k k k b b b,在理论上认为是相对比较好的初始点?

我们需要分析 y = k x + b y = kx + b y=kx+b 这个函数以及所有变量的性质,下面我们一个个分析

x x x:输入的数据

  • x x x 通常来自于统计得到的数据,来自于日常生活中产生的数据。我们可以通过数据产生的概率来描述它,我们认为 x x x 通常会满足极端数据少,平均数据多的情况
  • 既然 x x x 符合我们日常生活中的数据,那不妨我们使用正态分布来描述 x x x,这其实是对日常收集到的数据的一个假设,因为日常生活中的很多数据比如一个班级的成绩、身高、体重等都是符合正态分布的,因此,对 x x x 做正态分布假设是一个非常具有通用型的设计
  • 在之前的实现中,我们通常会对 x x x 做正则化,即 x -> normalize(x), (x - mean) / std,它其实是把 x x x 正则化到一个标准的正态分布下。因此最终的 x ∼ N ( 0 , 1 ) x\sim N\left(0,1\right) xN(0,1)

y y y:输出的值

  • y y y 是对于输入数据 x x x 的映射后的一个值,我们从统计的角度来看, y y y 依旧是随着 x x x 而言的,依旧是正态分布
  • y -> normalize(y), (y - mean) / std y ∼ N ( 0 , 1 ) y\sim N\left(0,1\right) yN(0,1)

因此我们可以得出结论,对于 k k k b b b 的设计而言,如果能够使得 x x x 经过 k k k b b b 过后的分布不产生变化,那么 k k k b b b 将会是一个比较好的初始化位置。

具体我们该如何设计 k k k b b b 的初始值使得分布不会产生变化呢?

在开始之前我们需要对 np.random.randn 有一个简单的了解

import numpy as np

x = np.random.randn(784, 256)

# mean -> 均值
# std  -> 标准差
# var  -> 方差
# 其中 var = std^2
print(x.mean(), x.std(), x.var())

如果对服从正态分布的变量乘以某个常数或者加上某个常数后的分布又是怎样的呢?

那这就不得不提关于正态分布的一些特性了

  • 如果 X ∼ N ( μ , σ 2 ) X\sim N\left(\mu,{\sigma}^2\right) XN(μ,σ2) a a a 是实数,那么 a X ∼ N ( a μ , ( a σ ) 2 ) aX \sim N\left(a\mu,(a\sigma)^2\right) aXN(aμ,()2)
  • 如果 X ∼ N ( μ , σ 2 ) X\sim N\left(\mu,{\sigma}^2\right) XN(μ,σ2) b b b 是实数,那么 X + b ∼ N ( μ + b , σ 2 ) X+b \sim N\left(\mu+b,{\sigma}^2\right) X+bN(μ+b,σ2)
  • 更多细节可参考 wiki

根据正态分布的上面两条特性我们可以完成对 k k k b b b 的初始值的设计了

首先 predict = x @ k + b,我们需要确保输入 predict 的分布与 x 保持一致都为标准正态分布。当输入 x 乘以 k 后其标准差为发生变化,我们目的当然是希望它不发生变化,乘完之后还是和 x 保持一致

我们先来看一个简单的例子:

x = np.random.randn(784, 256)
k = np.random.randn(256, 64)

predict = x @ k

# 0.04331550600632987 16.002218532647994 256.0709979666229
print(predict.mean(), predict.std(), predict.var())

可以看到二者相乘后依旧服从正态分布,其均值依旧是 0,而标准差则为 256 = 16 \sqrt {256} = 16 256 =16

为什么结果矩阵会服从均值为 0,标准差为 16 的正态分布呢?以下是 chatGPT 给出的解释

首先,对于标准正态分布的随机变量 x x x k k k,它们的均值为 0,标准差为 1

x x x k k k 相乘后,得到一个新的矩阵 m m m 其维度为 (784,64)。 m m m 中的每个元素 m ( i , j ) m (i,j) m(i,j) 是由 x x x 的第 i i i 行与 k k k 的第 j j j 列进行内积运算得到的

由于 x x x k k k 都服从标准正态分布,它们的每个维度都是独立的随机变量。在独立随机变量相乘的情况下,我们可以应用卷积和中心极限定理。

根据卷积和中心极限定理的原理,当进行大量独立随机变量的相乘时,结果的分布会趋近于正态分布。

在当前的情况下, m m m 中的每个元素都是由 256 个独立的随机变量相乘得到的,而这些随机变量都来自于标准正态分布,根据中心极限定理,这样的相乘操作会导致结果的分布趋近于正态分布。因此, m m m 的元素也会服从正态分布

对于均值的推导,由于 x x x k k k 的均值为 0,它们的内积的期望值也为 0。因此, m m m 的每个元素的期望值为 0,即 m m m 的均值为 0

对于标准差的推导,由于每个 x x x k k k 的元素独立的且标准差均为 1,内积的标准差会变为相乘元素个数的平方根。在当前情况下, x x x k k k 的维度均为 256,所以相乘后的标准差为 256 \sqrt {256} 256 ,即 16。

一切似乎变得清晰明了了呀😄,简单来说 m m m 中的每个元素都是由 256 个独立的随机变量相乘得到。而这些随机变量来自于标准正态分布

假设 X X X Y Y Y 服从标准正态分布且独立,现在需要计算它们的乘积 Z = X Y Z = XY Z=XY 的均值和标准差
E ( Z ) = E ( X Y ) = E ( X ) E ( Y ) = 0 V a r ( Z ) = V a r ( X Y ) = E [ ( X Y − E [ X Y ] ) 2 ] = E [ ( X Y ) 2 ] = E [ X 2 Y 2 ] = E ( X 2 ) E ( Y 2 ) = V a r ( X ) V a r ( Y ) = 1 \begin{aligned} E(Z) &=E(XY)=E(X)E(Y)=0 \\ Var(Z) &=Var(XY)=E[(XY - E[XY])^2] \\ &=E[(XY)^2]=E[X^2Y^2] \\ &=E(X^2)E(Y^2) \\ &= Var(X)Var(Y) \\ &= 1 \end{aligned} E(Z)Var(Z)=E(XY)=E(X)E(Y)=0=Var(XY)=E[(XYE[XY])2]=E[(XY)2]=E[X2Y2]=E(X2)E(Y2)=Var(X)Var(Y)=1
因此 256 个独立的均值为 0,方差为 1 的随机变量相加后的均值为 0,方差为 256,标准差为 16

根据正态分布的第一条特性,要想让其标准差变为 1,只需要除以 16 即可

D = 256
x = np.random.randn(784, D)
k = np.random.randn(D, 64)

predict = x @ k 
predict = predict / np.sqrt(D)
# 0.0029998421515664217 0.993879430767524 0.9877963229027776
print(predict.mean(), predict.std(), predict.var())

可以看到结果符合我们的预期,那么关于 k k k 的设计我们就清楚了

k k k 的设计: k ∼ N ( 0 , 1 D ) k \sim N(0, \frac{1}{\sqrt D}) kN(0,D 1) 其中 D = k.rows

接下来我们看下 b b b 的设计,根据正态分布特性第二条,当加上一个随机数时其均值会发生变化,而方差不变,为了让最终的 predictx 的分布保持一致,我们需要将这个均值去除,很简单,我们将 b b b 设置为 0 即可。

b b b 的设计:0

k k k b b b 的设计符合上述条件时,predict = x @ k + b 就会实现 x x x 经过 k k k b b b 后的分布不产生变化,满足我们的要求。

对于线性回归模型的权重初始化比较简单,而对于 BP 神经网络来说可能稍微复杂点

h = x @ k1 + b1
ha = nonlinear(h)
predict = ha @ k2 + b2

对于模型的参数设计而言,我们依旧希望 x x x 经过 k 1 k_1 k1 b 1 b_1 b1 n o n l i n e a r nonlinear nonlinear k 2 k_2 k2 b 2 b_2 b2 后,得到的 p r e d i c t predict predict 保持分布不变

由于 n o n l i n e a r nonlinear nonlinear 会改变分布的性质,因此在对 k 1 k_1 k1 b 1 b_1 b1 k 2 k_2 k2 b 2 b_2 b2 做初始化时需要将其考虑进去

对于 n o n l i n e a r nonlinear nonlinear 的权重初始化我们可以采用凯明初始化

凯明初始化

fan_in,fan_out

fan_in -> k.rows fan_out -> k.cols

mean = 0

std = gain / sqrt(fan_in + fan_out)

关于不同的非线性函数对应的增益可参考 pytorch 官网 torch.nn.init,非线性函数不同对应的增益也将不同,比如 ReLU 的增益为 2 \sqrt 2 2

关于凯明初始化的更多细节需要大家自行补充

  • Xavier 初始化 Paper:Understanding the difficulty of training deep feedforward neural networks

  • Kaiming 初始化 Paper:Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification

  • 几种常见的权重初始化方法

  • 一文搞懂深度网络初始化(Xavier and Kaiming initialization)

总结

本次课程从一个新奇的角度带我们了解了 BP 算法,我们可以将其理解为多模型的堆叠,将中间的隐藏层抽象出来,可以不断的叠加,也比较符合人类社会的阶级制度。但值得注意的是我们往往会将中间隐藏层的输出经过一个非线性变换,在本次课程中我们是使用 relu 函数来实现的,这样可以加强模型的非线性表达能力,增强模型的性能。
本次课程还学习了权重初始化的相关理论知识,我们从线性回归模型出发最终得出了 k k k b b b 的设计,二者的设计思路是确保 x x x 经过 k k k b b b 后其分布不发生变化。根据这个设计思路我们可以推广到 BP 神经网络中权重的设计,由此引出了 Kaming 初始化,其均值为 0,方差为 gain/(fan_in+fan_out),不同激活函数对应的增益也不尽相同。

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

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

相关文章

java新特性stream

stream Java 8 是一个非常成功的版本&#xff0c;这个版本新增的Stream&#xff0c;配合同版本出现的 Lambda &#xff0c;给我们操作集合&#xff08;Collection&#xff09;提供了极大的便利。 Stream将要处理的元素集合看作一种流&#xff0c;在流的过程中&#xff0c;借助…

PMP常用英文术语缩写总结(文字版+表格版+图片版)

PMP常用英文术语缩写总结&#xff08;文字版表格版图片版&#xff09; 文字版 PMBOK Project Management Body of Knowledge 项目管理知识体系 PMI Project Management Institute 项目管理协会 PMO Project Management Office 项目管理办公室 PMIS Project Management Inf…

Spring Boot 中的服务注册是什么,原理,如何使用

Spring Boot 中的服务注册是什么&#xff0c;原理&#xff0c;如何使用 Spring Boot 是一个非常流行的 Java 后端框架&#xff0c;它提供了许多便捷的功能和工具&#xff0c;使得开发者可以更加高效地开发微服务应用。其中&#xff0c;服务注册是 Spring Boot 微服务架构中非常…

代码源 线段树模板

线段树1 思路&#xff1a; 我们需要维护的东西是序列的最小值和最小值个数 这道题没有修改操作&#xff0c;因此不考虑修改 然后考虑Pushup 最小值很简单&#xff0c;直接取min 最小值个数怎么维护呢&#xff1f;考虑这个区间需要维护的值如何从左右两个区间获得 如果左右…

四.流程控制语句

1、条件语句 Go 编程语言中 if 条件语句的语法如下&#xff1a; 1、基本形式 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } If 在布尔表达式为 true 时&#xff0c;其后紧跟的语句块执行&#xff0c;如果false 则不执行。 package main import "fmt" fu…

Spring MVC是什么?详解它的组件、请求流程及注解

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 本文将讲解Spring MVC是什么&#xff0c;它的优缺点与九大组件&#xff0c;以及它的请求流程与常用的注解。 目录 一、Spring MVC是什…

计算机网络 - http协议 与 https协议(2)

前言 本篇介绍了构造http请求的的五种方式&#xff0c;简单的使用postman构造http请求&#xff0c;进一步了解https, 学习https的加密过程&#xff0c;了解对称密钥与非对称密钥对于加密是如何进行的&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流…

【数据科学和可视化】反思十年数据科学和可视化工具的未来

数据科学在过去十年中呈爆炸式增长&#xff0c;改变了我们开展业务的方式&#xff0c;并让下一代年轻人为未来的工作做好准备。但是这种快速增长伴随着对数据科学工作的不断发展的理解&#xff0c;这导致我们在如何使用数据科学从我们的大量数据中获得可操作的见解方面存在很多…

vue3实现一个简单的数字滚动效果

一、实现数字按步长递增的效果 1.实现思路 将这个组件封装起来&#xff0c;需要外部引用的文件传递两个值&#xff1a;指定数值 num 和 滚动持续时长 duration。首先设置一个增量 step&#xff0c;让数字按照这个增量来进行递增。然后设置一个定时器 setInterval&#xff0c;…

Flink集群部署总结

集群部署方式 Flink有两种部署方式&#xff0c;Standalone和Flink on Yarn集群部署方式。 Flink集群架构 Flink分布式架构是常见的主从结构&#xff0c;由JobManager和TaskManager组成。JobManager是大脑&#xff0c;负责接收、协调、分发Task到各个TaskManager&#xff0c;也…

靶场搭建——搭建pikachu靶场

搭建pikachu靶场 搭建pikachu靶场1、win11本机搭建步骤2、虚拟机win2012搭建步骤 我所碰见的问题以及解决方式&#xff1a; 搭建pikachu靶场 这里我所运用到的材料有&#xff1a;首先我最终是在虚拟机中环境为win2012和主机都搭建完成。 &#xff08;一个即可&#xff09; Ph…

在各数据库中使用 MERGE 实现插入避重 SQL

MERGE实现插入避重操作 前言 MERGE是一种在数据库管理系统中用于合并&#xff08;插入、更新或删除&#xff09;数据的SQL语句。它允许根据指定的条件将数据从一个表合并到另一个表中&#xff0c;同时避免重复插入或更新数据。 MERGE语句通常由以下几个关键字和子句组成&…

Spring Boot 中的服务消费

Spring Boot 中的服务消费 在分布式系统中&#xff0c;服务消费是一个很常见的场景。通过服务消费&#xff0c;可以将一个系统中的服务作为另一个系统中的组件来使用。Spring Boot 提供了很多工具来简化服务消费的过程&#xff0c;本文将深入探讨 Spring Boot 中的服务消费是什…

Java——《面试题——maven篇》

全文章节 Java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ Java——《面试题——SpringCloud》 Java——…

Redis数据库的简介、部署及常用命令

Redis数据库的简介、部署及常用命令 一、关系数据库与非关系型数据库概述1、关系型数据库2、非关系型数据库3、关系数据库与非关系型数据库区别4、非关系型数据库产生背景 二、Redis简介1、Redis服务器程序的单线程模型2、Redis的优点 三、Redis部署四、Redis 命令工具1、redis…

全概率公式和贝叶斯公式

人工智能和机器学习中经常听到一个著名公式&#xff1a;贝叶斯概率公式。早已忘记了&#xff0c;赶紧记一下笔记。 &#xff08;一&#xff09;全概率公式&#xff1a; 注意&#xff1a;全概率公式成立的前提是Bi是样本的划分 其证明过程如下&#xff1a; &#xff08;二&…

C++笔记之互斥锁,原子变量,条件变量对比

C笔记之互斥锁&#xff0c;原子变量&#xff0c;条件变量对比 code review! 目的&#xff1a;避免多线程间共享数据的竞态条件。 文章目录 C笔记之互斥锁&#xff0c;原子变量&#xff0c;条件变量对比1.std::mutex——互斥锁2.std::lock_guard3.std::unique_lock4.std::ato…

C++之lambda函数应用(一百四十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

如何看懂时序图(1):时序图基础知识

对于参考手册中经常出现的一些时序图&#xff0c;经常会让我摸不着头脑。比如对于Flash的时序图来说&#xff0c;要看懂的话&#xff0c;里面的每一个参数都得系统地学一遍&#xff0c;而且时序图中的一些符号也不太懂是什么意思。前一段时间调HyperRAM的时候&#xff0c;因为那…