BP神经网络(反向传播算法原理、推导过程、计算步骤)
BP神经网络
- 1、反向传播算法的原理
- 2、反向传播算法参数学习的推导
- 3、反向传播算法参数更新案例
- 3.1 反向传播的具体计算步骤
- 3.1.1 计算输出层的误差
- 3.1.2 计算隐藏层误差
- 3.1.3 根据神经元误差,更新神经元间偏置和神经元间的连接权重。
- 3.1.4 进一步后向传播
- 4、实战:神经网络分类器
1、反向传播算法的原理
反向传播算法的核心思想是将输出误差以某种形式通过隐藏层向输入层逐层反转,如下图所示。
反向传播算法在整个神经网络训练过程中发挥着重要的作用,它调整神经元之间的参数来学习样本中的规则,事实上权重存储了数据中存在的特征。在训练过程中,前向传播和后向传播相辅相成,如下图所示。
反向传播算法由Hinton于1986年在Nature的论文中提出。简单来说,反向传播主要解决神经网络在训练模型时的参数更新问题。假设神经网络如下图所示,为了简化推到过程,输入层只用了一个特征。同样,输出层也只有一个节点,隐藏层使用了两个节点。注意,在实际的神经网络中,唱吧z1z_1z1和h1h_1h1当作一个节点来画图(其中z1=xw1,h1=sigmoid(z1)z_1=xw_1,h_1=sigmoid(z_1)z1=xw1,h1=sigmoid(z1),注意也可以是其他激活函数),这里为了方便推到才把两者分开。
反向传播算法需要解决每条边对应的权值如何更新才能使得整个输出的损失函数最小。在反向传播算法中,对于每个输出节点,给定一个输入样例,会得到一个预测值,而这个预测值和真实值之间的差距可以定义为误差(类比“欠的钱”),是谁影响了欠钱的多少?很明显,在神经网络模型中,只有待求的参数w1,w2,...,wn{w_1,w_2,...,w_n}w1,w2,...,wn了。
如何衡量每个参数对误差的影响?我们定义一个敏感度:当参数wiw_iwi在某个很小的范围内变动时,误差变动了多少,数学表示为 ΔLΔwi\frac{\Delta L }{\Delta w_i}ΔwiΔL。考虑一般情况,即微分∂L∂wi\frac{\partial L}{\partial w_i}∂wi∂L,其中L表示损失函数(即误差),ΔL,∂L\Delta L,\partial LΔL,∂L均表示因为参数变化而引起的损失函数的微小变化。
这样我们就有了基础的微分表达式,也是反向传播所有推导公式的基础,其实ΔLΔwi\frac{\Delta L }{\Delta w_i}ΔwiΔL很有意思,因为不管最终L(w)是什么样子,ΔLΔwi\frac{\Delta L }{\Delta w_i}ΔwiΔL=定值。所以,假设Δwi\Delta w_iΔwi>0,那么该定值为负数的情况下,wiw_iwi增大的方向上,L(wi)L(w_i)L(wi)将减少,而该定值为正数时,wiw_iwi增大的方向上,L(wi)L(w_i)L(wi)将增大。
梯度下降的更新算法有:w:=w−η∂L∂wiw:=w-\eta \frac{\partial L}{\partial w_i}w:=w−η∂wi∂L,可以结合下图进行理解。所谓梯度,其本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。下图中,J(θ0,θ1)J(\theta _0,\theta _1)J(θ0,θ1)表示损失函数,θ0和θ1\theta_0和\theta_1θ0和θ1表示两个参数,利用梯度下降法对J(θ0,θ1)J(\theta _0,\theta _1)J(θ0,θ1)优化取最小值相当于从起始点开始总是沿着梯度最大的反方向(反方向用负号表示)移动一定的距离,移动距离的大小由学习率决定,正如图中黑色折线代表的路径,一步一步达到J(θ0,θ1)J(\theta _0,\theta _1)J(θ0,θ1)的最小值或局部最小值。
2、反向传播算法参数学习的推导
这里依然使用如下图所示的神经网络进行计算和推到。这个简单的神经网络图有助于理解反向传播算法的构建过程。虽然与真实的神经网络有一定的差距,但分析过程是大同小异的。
由图可知:
z1=w1xz2=w2xh1=11+e−z1h2=11+e−z2z3=w3h1+w4h2y=11+e−z3z_1=w_1x\\ z_2=w_2x\\ h_1=\frac{1}{1+e^{-z_1}}\\ h_2=\frac{1}{1+e^{-z_2}}\\ z_3=w_3h_1+w_4h_2\\ y=\frac{1}{1+e^{-z_3}} z1=w1xz2=w2xh1=1+e−z11h2=1+e−z21z3=w3h1+w4h2y=1+e−z31
假设已知输入x,我们就能根据这一系列公式求得y。接下来,我们需要定义损失函数,使用平方误差函数(只针对一次输入):
L=12(y−t)2L=\frac{1}{2}(y-t)^2 L=21(y−t)2
式中,t表示真实值,y表示预测值,根据前面的介绍,模型训练实际上是更新wiw_iwi,既然要更新wiw_iwi,就需要求解∂L∂wi\frac{\partial L}{\partial w_i}∂wi∂L。于是,对于wiw_iwi,根据链式求导法则,可以求得:
∂L∂w1=∂L∂y∂y∂z3∂z3∂h1∂h1∂z1∂z1∂w1\frac{\partial L}{\partial w_1} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} \frac{\partial z_1}{\partial w_1} ∂w1∂L=∂y∂L∂z3∂y∂h1∂z3∂z1∂h1∂w1∂z1
我们再求w3w_3w3:
∂L∂w3=∂L∂y∂y∂z3∂z3∂w3\frac{\partial L}{\partial w_3} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial w_3} ∂w3∂L=∂y∂L∂z3∂y∂w3∂z3
从中我们可以看到一些模式(规律)。实际上,对于w1w_1w1的更新,在它相关的路径上,每条边的后继和前继节点对应的就是偏导的分子和分母。w3w_3w3同样如此,它的相关边有三条(最后y指向L的关系边没有画出来),对应的链式法则也恰好有三个偏导。
继续细化上述公式,目前来看,这与反向传播似乎没有什么关系。的确,根据这些性质还不足以引出反向传播,让我们继续往下看。
因为偏导数中的每个函数映射都是确定的(函数已经确定),所以我们可以求出所有偏导数,于是有:
∂L∂w1=(y−t)⋅y⋅(1−y)⋅w3⋅h1⋅(1−h1)⋅x\frac{\partial L}{\partial w_1} =(y-t)\cdot y\cdot(1-y)\cdot w_3\cdot h_1\cdot (1-h_1)\cdot x ∂w1∂L=(y−t)⋅y⋅(1−y)⋅w3⋅h1⋅(1−h1)⋅x
式中,x,t由样本给定,而y,h1,w3y,h_1,w_3y,h1,w3都在计算y时能够得到,这就意味着所有变量都是已知的,可以直接求出∂L∂w1\frac{\partial L}{\partial w_1}∂w1∂L。那怎么会有前向和反向“传播”呢?
宏观上,可以考虑一个非常大型的神经网络,它的参数wiw_iwi可能有成千上万个,对于每个参数我们都要列处一个偏导公式吗?这显然不现实。因此,我们需要进一步挖掘它们共同的模式。
继续看下图:
假设我们加入第二个特征x2x_2x2,那么对应的w5w_5w5的更新,我们有如下公式:
∂L∂w5=∂L∂y∂y∂z3∂z3∂h1∂h1∂z1∂z1∂w5\frac{\partial L}{\partial w_5} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} \frac{\partial z_1}{\partial w_5} ∂w5∂L=∂y∂L∂z3∂y∂h1∂z3∂z1∂h1∂w5∂z1
对比一下w1w_1w1:
∂L∂w1=∂L∂y∂y∂z3∂z3∂h1∂h1∂z1∂z1∂w1\frac{\partial L}{\partial w_1} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} \frac{\partial z_1}{\partial w_1} ∂w1∂L=∂y∂L∂z3∂y∂h1∂z3∂z1∂h1∂w1∂z1
实际上,只有最后一个分母发生了变化。我们刚才也总结出了一个重要结论,每个偏导代表一条边,所以对于w5w_5w5的更新,前面四个偏导值都需要重新计算一遍,也就是虚线指出的部分,为了计算w5w_5w5,需要重新走过w1w_1w1的部分路径。
即使我们用输入(x1,x2)(x_1,x_2)(x1,x2)求出了每个节点(如z1,h1,z2,h2,z3,yz_1,h_1,z_2,h_2,z_3,yz1,h1,z2,h2,z3,y)的值,为了求出每个wiw_iwi的偏导,需要多次代入这些变量,于是产生了大量的冗余。
另外,对于每个wiw_iwi搜使用手工求偏导实在是太复杂了。事实上,在上面例子计算偏导的过程中,如果有中间变量
δj=∂L∂y∂y∂z3∂z3∂h1∂h1∂z1\delta _j=\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} δj=∂y∂L∂z3∂y∂h1∂z3∂z1∂h1
那么计算w1w_1w1和w5w_5w5时,只要有对应的δj⋅∂z1∂w1\delta _j\cdot \frac{\partial z_1}{\partial w_1}δj⋅∂w1∂z1和δj⋅∂z1∂w5\delta _j\cdot \frac{\partial z_1}{\partial w_5}δj⋅∂w5∂z1,对于中间的子状态只需要计算一次,而不是指数型增长。这大大减少了重复计算,降低了计算的成本。
这和递归记忆化搜索(自顶向下)以及动态规划(自底向上)的两种对偶形式很像,为了解决重复子问题,我们可以采用反向传播。如果能够定义出合适的子状态,且得出递推式,那么工作就完成了。
再来对比下w1w_1w1和w3w_3w3的偏导,继续寻找规律:
∂L∂w1=∂L∂y∂y∂z3∂z3∂h1∂h1∂z1∂z1∂w1∂L∂w3=∂L∂y∂y∂z3∂z3∂w3\frac{\partial L}{\partial w_1} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} \frac{\partial z_1}{\partial w_1}\\ \frac{\partial L}{\partial w_3} =\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3} \frac{\partial z_3}{\partial w_3} ∂w1∂L=∂y∂L∂z3∂y∂h1∂z3∂z1∂h1∂w1∂z1∂w3∂L=∂y∂L∂z3∂y∂w3∂z3
这两个式子中,只有前两部分是一样的,所以可以令δ1=∂L∂y∂y∂z3\delta ^1=\frac{\partial L}{\partial y} \frac{\partial y}{\partial z_3}δ1=∂y∂L∂z3∂y,这样处理的好处在于,求w3w_3w3时,可以有:
∂L∂w3=δ1∂z3∂w3\frac{\partial L}{\partial w_3}=\delta ^1\frac{\partial z_3}{\partial w_3} ∂w3∂L=δ1∂w3∂z3
求w1w_1w1时,可以有:
∂L∂w1=δ1∂z3∂h1∂h1∂z1∂z1∂w1\frac{\partial L}{\partial w_1} = \delta ^1\frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1} \frac{\partial z_1}{\partial w_1} ∂w1∂L=δ1∂h1∂z3∂z1∂h1∂w1∂z1
综合图来理解,δ1\delta ^1δ1表示聚集在z3z_3z3的误差,为什么是z3z_3z3?因为在这里刚好可以求出w3w_3w3的偏导;结合公式理解,就是公式中的公共部分(重复子问题)。
我们可以同样定义第二层的误差δ12\delta_1^2δ12表示聚集在z1z_1z1的误差,δ22\delta_2^2δ22表示聚集在z2z_2z2的误差。所以有:
δ12=δ1∂z3∂h1∂h1∂z1=δ1⋅w3⋅h1z1\delta_1^2=\delta^1\frac{\partial z_3}{\partial h_1} \frac{\partial h_1}{\partial z_1}=\delta^1\cdot w_3\cdot \frac{h_1}{z_1} δ12=δ1∂h1∂z3∂z1∂h1=δ1⋅w3⋅z1h1
对应地,w1w_1w1的偏导公式可以有:
∂L∂w1=δ12∂z1∂w1\frac{\partial L}{\partial w_1}=\delta_1^2\frac{\partial z_1}{\partial w_1} ∂w1∂L=δ12∂w1∂z1
对比w1,w5,w3w_1,w_5,w_3w1,w5,w3,可以得到:
∂L∂w1=δ12∂z1∂w1∂L∂w5=δ12∂z1∂w5∂L∂w3=δ1∂z3∂w3\frac{\partial L}{\partial w_1}=\delta_1^2\frac{\partial z_1}{\partial w_1}\\ \frac{\partial L}{\partial w_5}=\delta_1^2\frac{\partial z_1}{\partial w_5}\\ \frac{\partial L}{\partial w_3}=\delta_1\frac{\partial z_3}{\partial w_3} ∂w1∂L=δ12∂w1∂z1∂w5∂L=δ12∂w5∂z1∂w3∂L=δ1∂w3∂z3
它们都属于同一种形式,而δ2\delta^2δ2是由δ1\delta^1δ1加上对应的wiw_iwi求得,所以我们首要的目标是求出最后一层的δ1\delta ^1δ1,接着就能根据前一层的权值wiw_iwi求出前一层每个节点的δ2\delta^2δ2。更新公式都一样,用δ2\delta^2δ2乘以上一层的输出值而已,因为y1=h1w1+h2w2y_1=h_1w_1+h_2w_2y1=h1w1+h2w2是线性的,求偏导h1h_1h1得到w1w_1w1,求偏导w1w_1w1得到h1h_1h1。
至此,离真正的反向传播推导出的公式还差一点,继续看下图:
我们按照关系边的概念,可以知道w5w_5w5的关系边应该由虚线的边组成。所以δ2\delta^2δ2的更新不止和z3z_3z3有关系,还和z4z_4z4有关。此时损失函数由两部分组成,对应一个输入样例(x1,x2)(x_1,x_2)(x1,x2),有:
L=12(y1−t1)2+12(y2−t2)2L=\frac{1}{2}(y_1-t_1)^2+ \frac{1}{2}(y_2-t_2)^2 L=21(y1−t1)2+21(y2−t2)2
所以对L求偏导,由加法法则可以得到∂L∂w5=∂L∂y1+∂L∂y2\frac{\partial L}{\partial w_5}= \frac{\partial L}{\partial y_1}+ \frac{\partial L}{\partial y_2}∂w5∂L=∂y1∂L+∂y2∂L,即多个节点指向同一个节点时,把它们的偏导值加起来即可(损失函数就这么定义)。故
δj2=∂hj∂zj∑wij⋅δi1\delta _j^2=\frac{\partial h_j}{\partial z_j} \sum w_{ij}\cdot \delta ^1_i δj2=∂zj∂hj∑wij⋅δi1
3、反向传播算法参数更新案例
如下图所示的三层神经网络,其激活函数为sigmoid函数,下面以这个简单的三层神经元为例,计算反向传播算法。
神经网络输入、权重及偏置项初始值
神经元前向传播(这个比较简单,这里不过多介绍了,就是每个节点加权求和,在带入激活函数)
根据前面关于反向传播算法的简单推到,结合实例,可以进一步推出这个神经网络的反向传播公式如下:
Erro=Oj(1−Oj)(Tj−Oj)Errj=Oj(1−Oj)∑kErrkwjkErr_o=O_j(1-O_j)(T_j-O_j)\\ Err_j=O_j(1-O_j)\sum_{k}^{}Err_kw_{jk} Erro=Oj(1−Oj)(Tj−Oj)Errj=Oj(1−Oj)k∑Errkwjk
其中ErrOErr_OErrO表示输出层的误差,ErrjErr_jErrj表示中间隐藏层的误差,OjO_jOj表示当前神经元的输出值,TjT_jTj表示该数据样本的真实值(即标签)。应当明确,反向传播的目的是调整神经网络的权重和偏置,让模型学习到数据中的规律,因此,计算误差只是中间环节,最重要的是权重的更新,如下式所示:
wij=wij+λErrjOitj=tj+λErrjw_{ij}=w_{ij}+\lambda Err_jO_i\\ t_j=t_j+\lambda Err_j wij=wij+λErrjOitj=tj+λErrj
其中,wijw_{ij}wij为权重,tjt_jtj为偏置,λ\lambdaλ为学习率。
3.1 反向传播的具体计算步骤
3.1.1 计算输出层的误差
Err6=O6(1−O6)(Tj−O6)=0.474∗(1−0.474)∗(1−0.474)=0.1311Err_6=O_6(1-O_6)(T_j-O_6)=0.474*(1-0.474)*(1-0.474)=0.1311 Err6=O6(1−O6)(Tj−O6)=0.474∗(1−0.474)∗(1−0.474)=0.1311
3.1.2 计算隐藏层误差
Err4=O4(1−O4)Err6w46=0.332∗(1−0.332)∗0.1311∗(−0.3)=−0.0087Err_4=O_4(1-O_4)Err_6w_{46}=0.332*(1-0.332)*0.1311*(-0.3)=-0.0087 Err4=O4(1−O4)Err6w46=0.332∗(1−0.332)∗0.1311∗(−0.3)=−0.0087
Err5=O5(1−O5)Err6w56=0.525∗(1−0.525)∗0.1311∗(−0.2)=−0.0065Err_5=O_5(1-O_5)Err_6w_{56}=0.525*(1-0.525)*0.1311*(-0.2)=-0.0065 Err5=O5(1−O5)Err6w56=0.525∗(1−0.525)∗0.1311∗(−0.2)=−0.0065
3.1.3 根据神经元误差,更新神经元间偏置和神经元间的连接权重。
w46=w46+λErr6O4=−0.3+0.9∗0.1311∗0.332=−0.261w_{46}=w_{46}+\lambda Err_6O_4=-0.3+0.9*0.1311*0.332=-0.261 w46=w46+λErr6O4=−0.3+0.9∗0.1311∗0.332=−0.261
w56=w56+λErr6O5=−0.2+0.9∗0.1311∗0.525=−0.138w_{56}=w_{56}+\lambda Err_6O_5=-0.2+0.9*0.1311*0.525=-0.138 w56=w56+λErr6O5=−0.2+0.9∗0.1311∗0.525=−0.138
3.1.4 进一步后向传播
w35=w35+λErr5O3=0.2+0.9∗(−0.0065)∗1=0.194w_{35}=w_{35}+\lambda Err_5O_3=0.2+0.9*(-0.0065)*1=0.194 w35=w35+λErr5O3=0.2+0.9∗(−0.0065)∗1=0.194
按照相同的方法进一步计算其他参数更新后的值,如下表所示:
上述就是反向传播算法的实例。该三层神经网络虽然简单,但是反向传播的原理同样适用于更复杂的网络。
4、实战:神经网络分类器
数据集是印第安人糖尿病数据集,利用MLPClassifier
函数制作神经网络,实现糖尿病数据集分类
# 神经网络分类器
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
data_url="pima-indians-diabetes.csv"
df=pd.read_csv(data_url)
display(df)
X=df.iloc[:,0:8]
y=df.iloc[:,8]
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=0)clf=MLPClassifier(solver='sgd',alpha=1e-5,hidden_layer_sizes=(5,2),random_state=1)
clf.fit(X_train,y_train)print('训练集准确率:',accuracy_score(y_train,clf.predict(X_train)))
print('测试集准确率:',accuracy_score(y_test,clf.predict(X_test)))
只是简单演示算法,并没有过多的输出模型评估指标。
BP神经网络(反向传播算法原理、推导过程、计算步骤)相关推荐
- 神经网络反向传播算法原理笔记
神经网络是一种是基于生物学中神经网络的基本原理,在理解和抽象了人脑结构和外界刺激响应机制后,以网络拓扑知识为理论基础,模拟人脑的神经系统对复杂信息的处理机制的一种数学模型.该模型以并行分布的处理能力. ...
- 神经网络训练中的Tricks之高效BP(反向传播算法)
神经网络训练中的Tricks之高效BP(反向传播算法) zouxy09@qq.com http://blog.csdn.net/zouxy09 Tricks!这是一个让人听了充满神秘和好奇的词.对于我 ...
- 【机器学习笔记】神经网络反向传播算法 推导
神经网络反向传播算法 推导 (一) 概念及基本思想 (二)信息的前向传播 (三)误差反向传播 (1)输出层的权重参数更新 (2)隐藏层的权重参数更新 (3)输出层与隐藏层的偏置参数更新 (4)反向传播 ...
- 转载:BP(反向传播算法)公式推导及例题解析
首发于 深度学习与我的那些事 写文章 BP(反向传播算法)公式推导及例题解析 WILL 深度学习搬砖者 关注他 297 人 赞同了该文章 写在前面:最近赶上<模式识别>课程考试,就把B ...
- 深度学习中反向传播算法简单推导笔记
反向传播算法简单推导笔记 1.全连接神经网络 该结构的前向传播可以写成: z(1)=W(1)x+b(1)z^{(1)} = W^{(1)}x+b^{(1)}z(1)=W(1)x+b(1) a(1)=σ ...
- 神经网络之BP(反向传播算法)的原理介绍
反向传播是人工神经网络中的一个重要算法,这个方法可以对网络中所有权重计算损失函数的梯度,然后这个梯度会反馈给最优化方法,用来更新权值以最小化损失函数. 反向传播算法的原理 我们先直观的看一下反向传播的 ...
- BP神经网络反向传播手动推导
BP神经网络过程: 基本思想 BP算法是一个迭代算法,它的基本思想如下: 将训练集数据输入到神经网络的输入层,经过隐藏层,最后达到输出层并输出结果,这就是前向传播过程. 由于神经网络的输出结果与实际结 ...
- 神经网络反向传播算法推导
反向传播是多数神经网络进行参数更新的基本方法,它的本质是巧妙利用了高数中的链式法则,下面对这一方法进行推导: (1)符号说明 :神经元的激活函数 :神经网络的权重向量 :神经网络的偏置向量 :某层的输 ...
- 反向传播算法的理论基础,神经网络反向传播算法
1.如何理解神经网络里面的反向传播算法 反向传播算法(Backpropagation)是目前用来训练人工神经网络(Artificial Neural Network,ANN)的最常用且最有效的算法.其 ...
最新文章
- 生成树计数Matrix-Tree定理-数学
- Java——重载和重写
- SSM中配置log4J输出sql语句
- 苹果 App Store 申请和管理相关知识
- 单片机编程主函数的特点
- Gym100917 	A - Abstract Picture
- javascript中的字符串和数组的互转
- Qt工作笔记-在ListWidget中多线程检索数据
- python linkedlist,LinkedList在python中的实现
- 问题二十:C++全局debug “ray tracing图形”实例
- 在大多数情况下病毒入侵计算机系统以后,网络支付与安全练习题库
- InDesign CS3完全自学视频教程
- java ocr文字识别_java文字识别技术
- flash图形、影片剪辑、按钮区别
- 自动驾驶/机器人 SLAM算法 面经1
- 【安全牛学习笔记】扫描工具-Nikto
- 每日一个小技巧:1招教你提取伴奏怎么做
- 第六章 政策方案的规划与抉择
- Pycharm2018破解版破解教程 Pycharm2018安装激活永久破解详细教程
- python第三方包国内镜像网址
热门文章
- Linux CentOS7 VMware正则介绍、grep工具、egrep表达式
- Dockerfile 中文参考文档
- 火山小视频怎么伪原创 视频md5很慢
- 新手抖音怎么上热门 修改视频md5
- 快排为什么那样快(转)
- win732位能下oracle11g吗,win7旗舰版(64位)环境下oracle11g的安装方法 蓝天
- ORB-SLAM2代码详解05: 关键帧KeyFrame
- 神奇的 Flutter 文字动画-animated_text_kit
- [文献阅读报告][译]: Social-STGCNN:CVPR2020论文翻译以及解读
- vue3实现H5监听浏览器回退并阻止回退