神经网络原理与代码实现

神经网络原理与代码实现(从小白入门到系统性理解)

一、神经网络的基本组成与基本步骤

神经网络通常由输入层、隐藏层、输出层组成,隐藏层可以有多层,最基本的神经网络就是三层。

每个层由若干神经元构成,其中输入层神经元个数通常与特征数相关、输出层个数与类别数相同、隐藏层的神经元数可以自定义。

以最简单的三层全连接神经网络为例,如图1。
在这里插入图片描述
图1

神经网络的执行步骤:初始化 --> 前向传播 --> 计算损失 --> 反向传播 --> 更新权重 --> 迭代至结束条件

神经网络的目的:输入特征,映射到输出结果,寻找最优的参数,使得预测的损失最小,得到较为成功的模型。

二、前向传播forward pass

1、初始化参数

如上图1,一个三层神经网络,一纵列为一层。

输入层神经单元2个+偏置项1个,x1和x2是两个输入值;隐藏层一层,5个神经元,隐藏层每个神经元的输出为Oi;输出层1个,为y。

各层之间为全连接,每条连接边代表一个权重参数w。例子中有输入层权重和输出层权重。

每一层合起来,以及各层之间的权重系数合起来,就组成对应的矩阵。输入层与隐藏层之间的权重,为输入权重矩阵input_weights;隐藏层与输出层之间的权重,为输出权重矩阵output_weights。

参数的初始化方法有多种,根据需要合理设计,能够在一定程度上防止梯度消失、梯度爆炸,每一层网络的输出值不宜过小、过大。

几种初始化方法:
    零初始化 zero initialization:导致神经元输出相同,网络无法有效学习
    随机初始化 Random initialization: 打破对称性,但随机值过大则容易导致梯度爆炸。
    正态分布初始化 Normal distribution
    预训练模型初始化:利用预训练好的模型权重作为初始化。

初始化的代码实现如下:

import random
import math

def rand(a, b):
    # random()产生随机小数,乘以差值,则能产生a到b之间的随机数
    return (b-a) * random.random() + a

def make_matrix(m, n, fill=0.0):  # 生成一个m*n的矩阵
    mat = []
    for i in range(m):
        mat.append([fill] * n) # 浮点数
    return mat

class BP():
    def setup(self, ni, nh, no):
        # 初始化输入数据元的个数ni、隐藏层神经元个数nh、输出层神经元数no
        self.input_n = ni + 1  # 多加一个偏置神经元,提供可控输入修正
        self.hidden_n = nh 
        self.output_n = no
        # 初始化神经元
        self.input_cells = self.input_n * [1.0]
        self.hidden_cells = self.hidden_n * [1.0]
        self.output_cells = self.output_n * [1.0]
        
        '''
        全连接的,一个输入单元,与其他所有的隐藏层单元都有连接,每个连接有一个权重参数,则共有i*h个,为输入权重矩阵。
        输入单元*权重,对所有连接同一个下层单元的求和,就得到下层单元的值value。
        同理,隐藏层单元*输出层单元,则有输出权重矩阵
        '''
        # 初始化权重矩阵
        self.input_weights = make_matrix(self.input_n, self.hidden_n)
        self.output_weights = make_matrix(self.hidden_n, self.output_n)
        # 权重矩阵随机激活
        for i in range(self.input_n):
            for h in range(self.hidden_n):
                self.input_weights[i][h] = rand(-0.2, 0.2)  # 为何是这个范围?小值随机初始化,以防过大

        for h in range(self.hidden_n):
            for o in range(self.output_n):
                self.output_weights[h][o] = rand(-0.2, 0.2)

        '''
        矫正矩阵机制:为了加快学习的效率。
            记录上一次反向传播过程中的EjOi,即误差*神经元输出
        '''
        # 初始化矫正矩阵, 0初始化
        self.input_correction = make_matrix(self.input_n, self.hidden_n)
        self.output_correction = make_matrix(self.hidden_n, self.output_n)    

2、单个神经元的计算

每个神经元实质是一系列计算。如上图1隐藏层的第一个神经元,可看到一个神经元中进行了两步计算,一是求和,二是非线性激活。单独可看下图:
在这里插入图片描述

(1)求和

单个神经元,需要对与上一层连接的所有神经元的值进行加权求和,权就是边上的值w。求和公式:e =ΣWijXi + b(偏置,如果有)

以隐藏层第一个神经元为例,求和如下:
e = w11x1 + w21x2 + b

(2)激活

激活选用非线性激活,这样在多个隐藏层的情况下,神经网络才有了深度的意义,使网络能够拟合复杂非线性模型;否则,如果是线性激活,层数再多,线性嵌套后仍旧是一个线性,都等同于一个隐藏层。

常用的激活函数(activation function)有:sigmoid、ReLu、tanh等。不同函数的特性与优缺点不同。

代码实现中采用sigmoid函数作为网络的激活函数,具有非线性特性和稳定性,其导数在0-1之间,梯度的变化不会过大。
在这里插入图片描述

sigmoid函数的导数是:
在这里插入图片描述

推导过程:
在这里插入图片描述

前一步求和的结果e,经过激活函数f后,得到隐藏层第一个神经元的输出o1 = f(e),f就是sigmoid函数。

其余神经元计算同理,包括输出层神经元的输出,也是对所有连接的隐藏层神经元输出加权求和并激活。

直到获得输出层的预测输出,这个过程就是前向传播的过程。

代码实现如下,遍历每一个神经元进行计算:

    ## 前向传播网络
    def predict(self, inputs):
        # 激活输入层
        for i in range(self.input_n - 1):
            self.input_cells[i] = inputs[i]

        # 激活隐藏层  计算隐藏层的值,前向传播
        for j in range(self.hidden_n):
            total = 0.0
            for i in range(self.input_n):
                total += self.input_cells[i] * self.input_weights[i][j]
            # 激活函数激活,对前一层的累加结果进行激活
            self.hidden_cells[j] = sigmoid(total)
        # 计算输出层的值
        for k in range(self.output_n):
            total = 0.0
            for j in range(self.hidden_n):
                total += self.hidden_cells[j] * self.output_weights[j][k]
            self.output_cells[k] = sigmoid(total)
        
        return self.output_cells[:]

三、计算损失,反向传播(BackPropagation,BP)

1、什么是损失函数 Loss Function

每一个样本经过前向传播(forward)后,都会得到一个预测输出值。而每个样本应该有个标签值或真实值(GroundTruth)。
预测值与真实值之间的差值,就成为损失,损失值越小,证明模型越成功。
有许多不同种类的损失函数,经过pytorch、TensorFlow等库封装后,有了具体的名字。
学习的目的是让预测值无限接近真实值,因此损失函数可以作为衡量的方法。

常用损失函数:L2 Loss均方误差(Mean Squared Error, MSE)、L1 Loss平均绝对值误差(Mean Absolute Error,MAE)

代码实现中选取L2 Loss均方误差MSE。MSE的公式如下:
在这里插入图片描述

label为真实值,self.output_cells为预测值,代码如下:

        error = 0.0 
        for o in range(len(label)):
            error += 0.5 * (label[o] - self.output_cells[o]) ** 2

2、反向传播算法

反向传播算法,是“误差反向传播”的简称。建立在梯度下降算法的基础上。
网络为了找到合适的参数w、b,使得代价函数取值最小,利用梯度下降法求解代价函数的最优化问题。

梯度下降算法:找到目标函数L2 Loss的最小值。梯度是一个向量,目标函数在具体某点沿着梯度相反的方向下降最快。每次梯度下降都遍历整个数据集。每走一步就迭代更新一次。

在反向传播过程中,我们计算损失函数L相对于每个权重的梯度,并基于此更新权重。

推导如下:
其中,σ 是 Sigmoid 函数(对应前面的f), z 是线性组合(对应前面的e),a 是激活后的输出(对应前面的o), y ^ \hat{y} y^ 是预测值。

①计算输出层误差

损失函数

L = 1 2 ( y ^ − y ) 2 L=\frac{1}{2}(\hat{y}-y)^2 L=21(y^y)2

输出层误差项

δ o = ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ z o \delta_o = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_o} δo=y^Lzoy^
其中:
∂ L ∂ y ^ = y ^ − y \frac{\partial L}{\partial \hat{y}} = \hat{y} - y y^L=y^y
∂ y ^ ∂ z o = σ ′ ( z o ) \frac{\partial \hat{y}}{\partial z_o} = \sigma'(z_o) zoy^=σ(zo)

因此:
δ o = ( y ^ − y ) ⋅ σ ′ ( z o ) \delta_o = (\hat{y} - y) \cdot \sigma'(z_o) δo=(y^y)σ(zo)

由于激活函数sigmoid的导数为 σ ′ ( z o ) = σ ( z o ) ⋅ ( 1 − σ ( z o ) ) \sigma'(z_o) = \sigma(z_o) \cdot (1 - \sigma(z_o)) σ(zo)=σ(zo)(1σ(zo))

最终我们有输出层误差公式:
δ o = ( y ^ − y ) ⋅ y ^ ⋅ ( 1 − y ^ ) \delta_o = (\hat{y} - y) \cdot \hat{y} \cdot (1 - \hat{y}) δo=(y^y)y^(1y^)

即,预测误差 * sigmoid’(预测值)

实现代码如下:

        # 获取输出层误差
        output_deltas = [0.0] * self.output_n
        for o in range(self.output_n):
            error = label[o] - self.output_cells[o]  
            output_deltas[o] = sigmoid_derivate(self.output_cells[o]) * error 

②计算隐藏层误差
由于隐藏层没有label值,所以隐藏层误差使用下一层误差的加权和代替,进行反向传播:
δ h = ∑ o δ o w h o ⋅ σ ′ ( z h ) \delta_h = \sum_{o} \delta_o w_{ho} \cdot \sigma'(z_h) δh=oδowhoσ(zh)

其中:
σ ′ ( z h ) = σ ( z h ) ⋅ ( 1 − σ ( z h ) ) \sigma'(z_h) = \sigma(z_h) \cdot (1 - \sigma(z_h)) σ(zh)=σ(zh)(1σ(zh))

因此:
δ h = ( ∑ o δ o w h o ) ⋅ a h ⋅ ( 1 − a h ) \delta_h = \left( \sum_{o} \delta_o w_{ho} \right) \cdot a_h \cdot (1 - a_h) δh=(oδowho)ah(1ah)

即,隐藏层误差=连接的所有输出单元误差的加权和*sigmoid’(隐藏层输出值)

代码实现如下:

        # 获取隐藏层误差, 输出层的误差*输出权重,开始反向传播
        hidden_deltas = [0.0] * self.hidden_n
        for h in range(self.hidden_n):
            # 先计算每个error的值,因为隐藏层无label,使用下一层的误差的加权作为error
            error = 0.0
            for o in range(self.output_n):
                error += output_deltas[o] * self.output_weights[h][o]
            # 再计算每个隐藏层神经元的误差
            hidden_deltas[h] = sigmoid_derivate(self.hidden_cells[h]) * error

③更新权重
权重更新公式为:
w i j = w i j − η ∂ L ∂ w i j w_{ij} = w_{ij} - \eta \frac{\partial L}{\partial w_{ij}} wij=wijηwijL

根据链式法则:
∂ L ∂ w h o = δ o a h \frac{\partial L}{\partial w_{ho}} = \delta_o a_h whoL=δoah
∂ L ∂ w i h = δ h x i \frac{\partial L}{\partial w_{ih}} = \delta_h x_i wihL=δhxi

因此,权重更新公式为:
w h o = w h o − η δ o a h w_{ho} = w_{ho} - \eta \delta_o a_h who=whoηδoah
w i h = w i h − η δ h x i w_{ih} = w_{ih} - \eta \delta_h x_i wih=wihηδhxi

实际中,为了加快学习的效率,我们引入矫正矩阵机制,矫正矩阵记录上一次反向传播过程中的σo ah或σh xi, 记为Cij(将对应代码中的change变量),μ为矫正率参数,对应代码中的correct变量。

于是,公式变为:
w = w + η δ a + μ C i j w= w + \eta \delta a + μ Cij w=w+ηδa+μCij

代码实现如下:

        # 更新输出权重
        for h in range(self.hidden_n):
            for o in range(self.output_n):
                # Wij = Wij + λ Ej Oi + μCij    
                # 公式:W' = W + 学习率η或者λ * Δ(误差和σ * 偏导权重) * cells
                change = output_deltas[o] * self.hidden_cells[h]   # 输出误差 * 隐藏层的单元值
                self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]  
                self.output_correction[h][o] = change

        # 更新输入权重
        for i in range(self.input_n):
            for h in range(self.hidden_n):
                # Wij = Wij + λ [h] * self.input_cells[i]
                change = hidden_deltas[h] * self.input_cells[i]
                self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
                self.input_correction[i][h] = change   # 记录上一次的权重更新的EjOi,有记忆性,有点类似动量梯度下降法

3、整个反向传播过程

    ## 反向传播(误差反向传播),更新权重,返回最终预测误差
    def back_propagate(self, case, label, learn, correct):
        # 执行前馈
        self.predict(case)
        
        '''
        计算误差的方法有很多:误差平方和、熵Entropy、交叉熵
        '''
        # 获取输出层误差
        output_deltas = [0.0] * self.output_n
        for o in range(self.output_n):
            error = label[o] - self.output_cells[o]  ## 损失:真实值与预测值的差值,损失越小,模型越佳。pytorch、tensorflow库封装有不同的损失函数
            output_deltas[o] = sigmoid_derivate(self.output_cells[o]) * error  
        
        # 获取隐藏层误差, 输出层的误差*输出权重,开始反向传播
        hidden_deltas = [0.0] * self.hidden_n
        for h in range(self.hidden_n):
            # 先计算每个error的值,因为隐藏层无label,使用下一层的误差的加权作为error
            error = 0.0
            for o in range(self.output_n):
                error += output_deltas[o] * self.output_weights[h][o]
            # 再计算每个隐藏层神经元的误差
            hidden_deltas[h] = sigmoid_derivate(self.hidden_cells[h]) * error
        
        # 更新输出权重
        for h in range(self.hidden_n):
            for o in range(self.output_n):
                # Wij = Wij + λ Ej Oi + μCij    
                # 公式:W' = W + 学习率η或者λ * Δ(误差和σ * 偏导权重) * cells
                change = output_deltas[o] * self.hidden_cells[h]   # 输出误差 * 隐藏层的单元值
                self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]  
                self.output_correction[h][o] = change

        # 更新输入权重
        for i in range(self.input_n):
            for h in range(self.hidden_n):
                # Wij = Wij + λ [h] * self.input_cells[i]
                change = hidden_deltas[h] * self.input_cells[i]
                self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
                self.input_correction[i][h] = change   # 记录上一次的权重更新的EjOi,有记忆性,类似动量梯度下降法

        ### 获取全局误差
        error = 0.0 
        for o in range(len(label)):
            error += 0.5 * (label[o] - self.output_cells[o]) ** 2
        
        return error

四、迭代至结束

每轮次epoch都会遍历训练样本,每个样本都会前向传播一次以及反向传播一次,并更新一次整个网络权重。

学习率(learning rate,lr):控制模型的学习进度, 也叫步长stride。反向传播中的η。

学习率
学习速度
使用时间刚开始训练时一定轮数后
缺点易损失值爆炸;易震荡易过拟合;收敛速度慢

代码实现中,采用0.05的学习率,无学习率衰减策略。

代码实现:

    ## 训练迭代,cases为训练数据输入集,labels为训练数据标签集,可以修改最大迭代次数limit, learn学习率λ(一般在0-0.1上取值), correct矫正率μ三个参数
    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
        # 设置了最大迭代次数,最简单的训练终止条件,不能保证训练结果的精确度
        for i in range(limit):
            error = 0.0
            # 每一个训练样本都会更新一次整个网络的参数
            for i in range(len(cases)):
                label = labels[i]
                case = cases[i]
                error += self.back_propagate(case, label, learn, correct)


    def test(self):
        # 训练数据的输入集
        cases = [
                [0, 0],
                [0, 1],
                [1, 0],
                [1, 1],
            ]
        # 训练数据的标签集
        labels = [[0], [1], [1], [0]]
        self.setup(2, 5, 1)  # 设置各层的神经元数量
        self.train(cases, labels, 10000, 0.5, 0.1)
        for case in cases:
            print(self.predict(case))

五、神经网络整体实现代码

该神经网络的目的是学习异或逻辑,相同为0,相异为1.输入的数据集为四个,分别为(0,0),(0,1),(1,0),(1,1)。对应的标签分别为0,1,1,0.

网络的预测结果输出为:
在这里插入图片描述

import random
import math

def rand(a, b):
    # random()产生随机小数,乘以差值,则能产生a到b之间的随机数
    return (b-a) * random.random() + a

def make_matrix(m, n, fill=0.0):  # 生成一个m*n的矩阵
    mat = []
    for i in range(m):
        mat.append([fill] * n) # 浮点数
    return mat

# 传入的x,实际为神经元中计算上一层得到的累加和,然后激活
def sigmoid(x): # sigmoid函数
    return 1.0 / (1.0 + math.exp(-x))
# sigmoid' = sigmoid * (1-sigmoid), 这里传入的x需是sigmoid(原始x),即经过激活后的值,与上一个x不同
def sigmoid_derivate(x):  # sigmoid的导数
    return x * (1-x)

class BP():
    def setup(self, ni, nh, no):
        # 初始化输入数据元的个数、隐藏层个数?、输出层单元数
        self.input_n = ni + 1  # why? 多加一个偏置神经元,提供可控输入修正?如何理解?
        self.hidden_n = nh 
        self.output_n = no
        # 初始化神经元
        self.input_cells = self.input_n * [1.0]
        self.hidden_cells = self.hidden_n * [1.0]
        self.output_cells = self.output_n * [1.0]
        
        '''
        全连接的,一个输入单元,与其他所有的隐藏层单元都有连接,每个连接有一个权重参数,则共有i*h个,为输入权重矩阵。
        输入单元*权重,对所有连接同一个下层单元的求和,就得到下层单元的值value。
        同理,隐藏层单元*输出层单元,则有输出权重矩阵
        '''
        # 初始化权重矩阵
        self.input_weights = make_matrix(self.input_n, self.hidden_n)
        self.output_weights = make_matrix(self.hidden_n, self.output_n)
        # 权重矩阵随机激活
        for i in range(self.input_n):
            for h in range(self.hidden_n):
                self.input_weights[i][h] = rand(-0.2, 0.2)  # 为何是这个范围?

        for h in range(self.hidden_n):
            for o in range(self.output_n):
                self.output_weights[h][o] = rand(-0.2, 0.2)

        '''
        合理设计初始化,防止梯度消失、梯度爆炸,每一层的网络输出值不能太大也不能太小。均匀分布初始化会爆炸?
        零初始化zero initialization:导致神经元输出相同,网络无法有效学习
        随机初始化 Random initialization: 打破对称性,但随机值过大则容易导致梯度爆炸
        正态分布初始化 Normal distribution
        预训练模型初始化:利用预训练好的模型权重作为初始化。
        以上均为找来的结论,我自己没有实践验证,俗称答案。
        '''

        '''
        矫正矩阵机制:为了加快学习的效率。
            记录上一次反向传播过程中的EjOi,即误差*神经元输出
        '''
        # 初始化矫正矩阵, 0初始化
        self.input_correction = make_matrix(self.input_n, self.hidden_n)
        self.output_correction = make_matrix(self.hidden_n, self.output_n)

    ## 前向传播网络
    def predict(self, inputs):
        # 激活输入层
        for i in range(self.input_n - 1):
            self.input_cells[i] = inputs[i]

        # 激活隐藏层  计算隐藏层的值,前向传播
        for j in range(self.hidden_n):
            total = 0.0
            for i in range(self.input_n):
                total += self.input_cells[i] * self.input_weights[i][j]
            # 激活函数激活,对前一层的累加和进行激活
            self.hidden_cells[j] = sigmoid(total)
        # 计算输出层的值
        for k in range(self.output_n):
            total = 0.0
            for j in range(self.hidden_n):
                total += self.hidden_cells[j] * self.output_weights[j][k]
            self.output_cells[k] = sigmoid(total)
        
        return self.output_cells[:]


    ## 反向传播(误差反向传播),更新权重,返回最终预测误差
    def back_propagate(self, case, label, learn, correct):
        # 执行前馈
        self.predict(case)
        
        '''
        计算误差的方法有很多:误差平方和、熵Entropy、交叉熵
        '''
        # 获取输出层误差
        output_deltas = [0.0] * self.output_n
        for o in range(self.output_n):
            error = label[o] - self.output_cells[o]  ## 损失:真实值与预测值的差值,损失越小,模型越佳。pytorch、tensorflow库封装有不同的损失函数
            output_deltas[o] = sigmoid_derivate(self.output_cells[o]) * error   ## 为何求导?因为反向更新权重的时候,需要计算每个权重的偏导数,起缩放因子作用,占比不同。实质是损失函数L相对于每个权重的梯度,推出的整体公式,并非是只单纯地对sigmoid求导。
        
        # 获取隐藏层误差, 输出层的误差*输出权重,开始反向传播
        hidden_deltas = [0.0] * self.hidden_n
        for h in range(self.hidden_n):
            # 先计算每个error的值,因为隐藏层无label,使用下一层的误差的加权作为error
            error = 0.0
            for o in range(self.output_n):
                error += output_deltas[o] * self.output_weights[h][o]
            # 再计算每个隐藏层神经元的误差
            hidden_deltas[h] = sigmoid_derivate(self.hidden_cells[h]) * error
        
        # 更新输出权重
        for h in range(self.hidden_n):
            for o in range(self.output_n):
                # Wij = Wij + λ Ej Oi + μCij    
                # 公式:W' = W + 学习率η或者λ * Δ(误差和σ * 偏导权重) * cells
                change = output_deltas[o] * self.hidden_cells[h]   # 输出误差 * 隐藏层的单元值
                self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]  
                ## why don't renew the output_correction here
                self.output_correction[h][o] = change

        # 更新输入权重
        for i in range(self.input_n):
            for h in range(self.hidden_n):
                # Wij = Wij + λ [h] * self.input_cells[i]
                change = hidden_deltas[h] * self.input_cells[i]
                self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
                self.input_correction[i][h] = change   # 记录上一次的权重更新的EjOi,有记忆性,类似动量梯度下降法?

        ### 获取全局误差
        '''
        损失函数/代价函数
        均方误差MSE,Mean Squared Error  = 1/2 Σ(y - y尖)²
        目的是:找到合适的参数w,b,使得代价函数的取值最小,监督学习问题就转变为最优化问题。常用的最优化实现算法是,梯度下降算法
        相较于设置最大训练次数,更好的办法是使用损失函数(loss function)作为终止训练的依据
        '''
        error = 0.0 
        for o in range(len(label)):
            error += 0.5 * (label[o] - self.output_cells[o]) ** 2
        
        return error


    ## 训练迭代,cases为训练数据输入集,labels为训练数据标签集,可以修改最大迭代次数limit, learn学习率λ(一般在0-0.1上取值), correct矫正率μ三个参数
    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
        # 设置了最大迭代次数,最简单的训练终止条件,不能保证训练结果的精确度
        for i in range(limit):
            error = 0.0
            # 每一个训练样本都会更新一次整个网络的参数
            for i in range(len(cases)):
                label = labels[i]
                case = cases[i]
                error += self.back_propagate(case, label, learn, correct)


    def test(self):
        # 训练数据的输入集
        cases = [
                [0, 0],
                [0, 1],
                [1, 0],
                [1, 1],
            ]
        # 训练数据的标签集
        labels = [[0], [1], [1], [0]]
        self.setup(2, 5, 1)  # 设置各层的神经元数量
        self.train(cases, labels, 10000, 0.5, 0.1)
        for case in cases:
            print(self.predict(case))

bp = BP()
bp.test()

######  下一步计划,利用pytorch实现一个bp

六、参考资料
反向传播原理:https://blog.csdn.net/fsfjdtpzus/article/details/106256925
BP神经网络的实现:https://blog.csdn.net/qq_43350003/article/details/106986510
激活函数sigmoid:https://www.cnblogs.com/chenlin163/p/7676939.html
梯度下降法:https://blog.csdn.net/weixin_36811328/article/details/81348350
动量梯度下降法:https://blog.csdn.net/weixin_36811328/article/details/83451096
损失函数:https://blog.csdn.net/weixin_57643648/article/details/122704657
https://blog.csdn.net/qq_40280673/article/details/134293907
学习率:https://blog.csdn.net/jningwei/article/details/79243800
初始化方法:https://blog.csdn.net/u011852872/article/details/120407182

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值