最近接触目标检测较多,再此对最基本的神经网络知识进行补充,本博客适合想入门人工智能、其含有线性代数及高等数学基础的人群观看
1.构成
由输入层、隐藏层、输出层、激活函数、损失函数组成。
- 输入层:接收原始数据
- 隐藏层:进行特征提取和转换
- 输出层:输出预测结果
- 激活函数:非线性变换
- 损失函数:衡量模型预测结果与真实值之间的差距
2.正向传播过程
基础的神经网络如下图所示,其中层1为输入层,层2为隐藏层,层3为输出层:
每一个圆圈代表了一个神经元,各层的神经元各自相连,如图中的绿色箭头。每一条相连的绿线上拥有起始设定好的权重。隐藏层的神经元后跟着激活函数,进行信号的转变。
对于每一层信号的输入输出,均有以下公式表达,X为此层的输入,O为此层的输出,一般输入层采用激活函数,即输入即为输出。
X
=
W
⋅
I
n
p
u
t
O
=
s
i
g
m
o
i
d
(
X
)
X=WInput O=sigmoid(X)
X=W⋅InputO=sigmoid(X)
I
n
p
u
t
Input
Input 为输入矩阵,此处以如下为例:
I
n
p
u
t
=
[
1.0
0.5
0.35
]
Input = begin{bmatrix} 1.0 0.5 0.35 end{bmatrix}
Input=
1.00.50.35
W
W
W 为权重矩阵,各层的权重各不相同
W
=
[
w
1.1
w
1.2
w
1.3
w
2.1
w
2.2
w
2.3
w
3.1
w
3.2
w
3.3
]
W= begin{bmatrix} w_{1.1} & w_{1.2} &w_{1.3} w_{2.1} & w_{2.2} &w_{2.3} w_{3.1} & w_{3.2} &w_{3.3} end{bmatrix}
W=
w1.1w2.1w3.1w1.2w2.2w3.2w1.3w2.3w3.3
s
i
g
m
o
i
d
sigmoid
sigmoid 为激活函数
y
=
1
1
+
e
−
x
y=frac{1}{1+e^{-x}}
y=1+e−x1
过程演示(3层)
1.输入层: 由于输入层一般不使用激活函数,输入层的输出即为输入数据
I
n
p
u
t
Input
Input。
2.隐藏层: 此层的输入为:
X
h
i
d
d
e
n
=
W
i
n
p
u
t
2
h
i
d
d
e
n
⋅
I
n
p
u
t
=
[
w
1.1
w
1.2
w
1.3
w
2.1
w
2.2
w
2.3
w
3.1
w
3.2
w
3.3
]
⋅
[
1.0
0.5
0.35
]
X_{hidden}=W_{input2hidden} Input= begin{bmatrix} w_{1.1} & w_{1.2} &w_{1.3} w_{2.1} & w_{2.2} &w_{2.3} w_{3.1} & w_{3.2} &w_{3.3} end{bmatrix} begin{bmatrix} 1.0 0.5 0.35 end{bmatrix}
Xhidden=Winput2hidden⋅Input=
w1.1w2.1w3.1w1.2w2.2w3.2w1.3w2.3w3.3
⋅
1.00.50.35
此层的输出为:
O
h
i
d
d
e
n
=
s
i
g
m
o
i
d
(
X
h
i
d
d
e
n
)
=
1
1
+
e
X
h
i
d
d
e
n
O_{hidden} = sigmoid(X_{hidden})=frac{1}{1+e^{X_{hidden}}}
Ohidden=sigmoid(Xhidden)=1+eXhidden1
3.输出层: 输出层永远不使用激活函数,输出层的输出即为输入,输出层的输入为:
X
o
u
t
p
u
服务器托管网
t
=
W
h
i
d
d
e
n
2
o
u
t
p
u
t
⋅
O
h
i
d
d
e
n
X_{output} = W_{hidden2output}O_{hidden}
Xoutput=Whidden2output⋅Ohidden
3.激活函数
上文使用的是
s
i
g
m
o
i
d
sigmoid
sigmoid函数作为激活函数,还可以将其根据具体应用,更换为以下函数:
- Sigmoid函数:将输入值压缩到0到1之间,常用于二分类问题
- ReLU函数:将负值置为0,常用于深度神经网络中
- Tanh函数:将输入值压缩到-1到1之间,常用于回归问题
- Leaky ReLU函数:对负值进行微小的缩放,避免梯度消失问题
4.反向传播过程
误差计算:目标值-实际值
e
n
=
t
n
−
o
n
e_n = t_n – o_n
en=tn−on
下面以单个神经元返回误差为例:
对于最后输出的误差我们需要将他根据前一层的权重传播到前一层,以上面单个神经元的反向传播过程为例。传回1号神经元的误差为
e
r
r
o
r
s
⋅
w
1
w
1
+
w
2
errorsfrac{w_1}{w_1+w_2}
errors⋅w1+w2w1 ,传回2号神经元的误差为
e
r
r
o
r
s
⋅
w
2
w
1
+
w
2
errorsfrac{w_2}{w_1+w_2}
errors⋅w1+w2w2 。
过程演示(3层)
下面我们把这个过程放到三层的神经网络中分析:
我们以第二层第一个神经元为例,分析误差传播到此的值。
e
h
i
d
d
e
n
1
=
e
o
u
t
p
u
t
1
⋅
w
1.1
w
1.1
+
w
2.1
+
w
3.1
+
e
o
u
t
p
u
t
2
⋅
w
1.2
w
1.2
+
w
2.2
+
w
3.2
+
e
o
u
t
p
u
t
3
⋅
w
1.3
w
1.3
+
w
2.3
+
w
3.3
e_{hidden1} = e_{output1}frac{w_{1.1}}{w_{1.1}+w_{2.1}+w_{3.1}}+e_{output2}frac{w_{1.2}}{w_{1.2}+w_{2.2}+w_{3.2}}+e_{output3}frac{w_{1.3}}{w_{1.3}+w_{2.3}+w_{3.3}}
ehidden1=eoutput1⋅w1.1+w2.1+w3.1w1.1+eoutput2⋅w1.2+w2.2+w3.2w1.2+eoutput3⋅w1.3+w2.3+w3.3w1.3
接下来我们使用矩阵来表达这个麻烦的公式:
输出层误差:
e
r
r
o
r
o
u
t
p
u
t
=
(
e
1
e
2
e
3
)
error_{output}=begin{pmatrix} e_1 e_2 e_3 end{pmatrix}
erroroutput=
e1e2e3
隐藏层误差:
e
r
r
o
r
h
i
d
d
e
n
=
[
w
1.1
w
1.1
+
w
2.1
+
w
3.1
w
1.2
w
1.2
+
w
2.2
+
w
3.2
w
1.3
w
1.3
+
w
2.3
+
w
3.3
w
2.1
w
1.1
+
w
2.1
+
w
3.1
w
2.2
w
1.2
+
w
2.2
+
w
3.2
w
2.3
w
1.3
+
w
2.3
+
w
3.3
w
3.1
w
1.1
+
w
2.1
+
w
3.1
w
3.2
w
1.2
+
w
2.2
+
w
3.2
w
3.3
w
1.3
+
w
2.3
+
w
3.3
]
⋅
e
r
r
o
r
o
u
t
p
u
t
error_{hidden}=begin{bmatrix} frac{w_{1.1}}{w_{1.1}+w_{2.1}+w_{3.1}} &frac{w_{1.2}}{w_{1.2}+w_{2.2}+w_{3.2}} &frac{w_{1.3}}{w_{1.3}+w_{2.3}+w_{3.3}} frac{w_{2.1}}{w_{1.1}+w_{2.1}+w_{3.1}} &frac{w_{2.2}}{w_{1.2}+w_{2.2}+w_{3.2}} &frac{w_{2.3}}{w_{1.3}+w_{2.3}+w_{3.3}} frac{w_{3.1}}{w_{1.1}+w_{2.1}+w_{3.1}} &frac{w_{3.2}}{w_{1.2}+w_{2.2}+w_{3.2}} &frac{w_{3.3}}{w_{1.3}+w_{2.3}+w_{3.3}} end{bmatrix} error_{output}
errorhidden=
w1.1+w2.1+w3.1w1.1w1.1+w2.1+w3.1w2.1w1.1+w2.1+w3.1w3.1w1.2+w2.2+w3.2w1.2w1.2+w2.2+w3.2w2.2w1.2+w2.2+w3.2w3.2w1.3+w2.3+w3.3w1.3w1.3+w2.3+w3.3w2.3w1.3+w2.3+w3.3w3.3
⋅erroroutput
去归一化:
e
r
r
o
r
h
i
d
d
e
n
=
[
w
1.1
w
1.2
w
1.3
w
2.1
w
2.2
w
2.3
w
3.1
w
3.2
w
3.3
]
⋅
e
r
r
o
r
o
u
t
p
u
t
=
w
h
i
d
d
e
n
2
o
u
t
p
u
t
⋅
e
r
r
o
r
o
u
t
p
u
t
error_{hidden}=begin{bmatrix} w_{1.1} & w_{1.2} & w_{1.3} w_{2.1} & w_{2.2} & w_{2.3} w_{3.1} & w_{3.2} & w_{3.3} end{bmatrix} error_{output} = w_{hidden2output}error_{output}
errorhidden=
w1.1w2.1w3.1w1.2w2.2w3.2w1.3w2.3w3.3
⋅erroroutput=whidden2output⋅erroroutput
5.更新权重
下一步需要取得误差最小的权重作为最优权重,在此我们使用梯度下降的方法找到误差最小时的权重。
梯度下降: 用于计算函数的最小值。随机起始点,通过导数的正负判断方向,朝着函数减小的方向,一步步增加x,并计算他的导数当导数为零或为设定范围内,取得最小值;否则继续增加。
在神经网络中由于x为权重矩阵,我们使用的梯度下降为多维梯度下降。
设定误差函数
在此例中我们使用
E
=
(
t
n
−
o
n
)
2
E = (t_n-o_n)^2
E=(tn−on)2
误差函数的斜率
∂
E
∂
w
i
j
=
∂
∂
w
i
j
∑
n
(
t
n
−
o
n
)
2
frac{partial E}{partial w_{ij}}=frac{partial}{partial w_{ij}}sum_n(t_n-o_n)^2
∂wij∂E=∂wij∂n∑(tn−on)2
由于在这里
o
n
o_n
on 仅取决于连接着的权重,所以误差函数的斜率可以改写为:
∂
∂
w
i
j
(
t
n
−
o
n
)
2
frac{partial}{partial w_{ij}}(t_n-o_n)^2
∂wij∂(tn−on)2
根据导数的链式法则,我们改写斜率函数:
∂
E
∂
w
i
j
=
∂
E
∂
o
n
∂
o
n
∂
w
i
j
=
−
2
(
t
n
−
o
n
)
∂
o
n
∂
w
i
j
frac{partial E}{partial w_{ij}}=frac{partial E}{partial o_n}times frac{partial o_n}{partial w_{ij}}=-2(t_n-o_n)frac{partial o_n}{partial w_{ij}}
∂wij∂E=∂on∂E∂wij∂on=−2(tn−on)∂wij∂on
我们再将
o
n
o_n
on带入到此函数
o
n
=
s
i
g
m
o
i
d
(
∑
j
w
j
,
k
⋅
o
j
)
o_n=sigmoid(sum_j w_{j,k}o_j)
on=sigmoid(∑jwj,k⋅oj),
o
j
o_j
oj为前一层的输出,得到函数如下:
斜率函数
=
−
2
(
t
n
−
o
n
)
∂
∂
w
i
,
j
s
i
g
m
o
i
d
(
∑
j
w
j
k
⋅
o
j
)
斜率函数 = -2(t_n-o_n)frac{partial}{partial w_{i,j}}sigmoid(sum_j w_{jk}o_j)
斜率函数=−2(tn−on)∂wi,j∂sigmoid(j∑wjk⋅oj)
我们对sigmoid函数进行微分:
∂
s
i
g
m
o
i
d
(
x
)
∂
x
=
s
i
g
m
o
i
d
(
x
)
(
1
−
s
i
g
m
o
i
d
(
x
)
)
frac{partial sigmoid(x)}{partial x} = sigmoid(x)(1-sigmoid(x))
∂x∂sigmoid(x)=sigmoid(x)(1−sigmoid(x))
我们再把它放到斜率函数之中:
斜率函数
=
−
2
⋅
(
t
n
−
o
n
)
⋅
s
i
g
m
o
i
d
(
∑
j
w
j
k
⋅
o
j
)
⋅
(
1
−
∑
j
w
j
k
⋅
o
j
)
⋅
∂
∂
w
i
.
j
(
∑
j
w
j
k
⋅
o
j
)
=
−
2
⋅
(
t
n
−
o
n
)
⋅
s
i
g
m
o
i
d
(
∑
j
w
j
k
⋅
o
j
)
⋅
(
1
−
∑
j
w
j
k
⋅
o
j
)
⋅
o
j
斜率函数=-2(t_n-o_n)sigmoid(sum_jw_{jk}o_j)(1-sum_jw_{jk}o_j)frac{partial }{partial w_{i.j}}(sum_jw_{jk}o_j) =-2(t_n-o_n)sigmoid(sum_jw_{jk}o_j)(1-sum_jw_{jk}o_j)o_j
斜率函数=−2⋅(tn−on)⋅sigmoid(j∑wjk⋅oj)⋅(1−j∑wjk⋅oj)⋅∂wi.j∂(j∑wjk⋅oj)=−2⋅(tn−on)⋅sigmoid(j∑wjk⋅oj)⋅(1−j∑wjk⋅oj)⋅oj
由于在此过程中我们只需判断斜率方向,我们可以把常数去除,即:
斜率函数
=
−
(
t
n
−
o
n
)
⋅
s
i
g
m
o
i
d
(
∑
j
w
j
k
⋅
o
j
)
⋅
(
1
−
∑
j
w
j
k
⋅
o
j
)
⋅
o
j
斜率函数=-(t_n-o_n)sigmoid(sum_jw_{jk}o_j)(1-sum_jw_{jk}o_j)o_j
斜率函数=−(tn−on)⋅sigmoid(j∑wjk⋅oj)⋅(1−j∑wjk⋅oj)⋅oj
我们根据已有的关系对斜率在此修改:
-
(
t
n
−
o
n
)
(t_n – o_n)
(
目标值
−
实际值
)
(目标值-实际值)
e
i
e_i
-
∑
i
w
i
,
j
⋅
o
i
sum_i w_{i,j}o_i
-
o
i
o_i
∂
E
∂
w
i
j
=
−
e
i
⋅
s
i
g
m
o
i
d
(
∑
i
w
i
j
o
i
)
⋅
(
1
−
s
i
g
m
o
i
d
(
∑
i
w
i
j
o
i
)
)
⋅
o
i
frac{partial E}{partial w_{ij}}=-e_i cdot sigmoid(sum_i w_{ij}o_i)cdot (1-sigmoid(sum_i w_{ij}o_i))cdot o_i
∂wij∂E=−ei⋅sigmoid(i∑wijoi)⋅(1−sigmoid(i∑wijoi))⋅oi
更新权重
有了误差函数的斜率,我们就可以通过梯度下降的方式更新权重,其中
alpha
为设定好的学习率:
W
n
e
w
=
W
o
l
d
−
∂
E
∂
w
i
j
W_{new} = W_{old}-alpha frac{partial E}{partial w_{ij}}
Wnew=Wold−∂wij∂E
权重的矩阵变化
w
i
j
=
⋅
E
k
⋅
o
k
⋅
(
1
−
o
k
)
⋅
o
j
Delta w_{ij} = alpha cdot E_k cdot o_k cdot (1-o_k) cdot o_j
wij=⋅Ek⋅ok⋅(1−ok)⋅oj
6.代码实现
神经网络代码应该由三部分组成:初始化函数、训练函数、查询函数
- 初始化函数:应该包含各层的节点数,学习率,随机权重矩阵以及激活函数
- 训练函数:应该包含正、反向传播,权重更新
- 查询函数:正向传播过程
import numpy.random
import scipy.special
# 激活函数设置
def activation_function(x):
return scipy.special.expit(x)
# 神经网络类
class NeuralNetwork:
# 初始化函数
def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
# 输入层、隐含层、输出层节点数
self.inodes = inputnodes
self.hnodes = hiddennodes
self.onodes = outputnodes
# 学习率
self.lr = learningrate
# 随机权重矩阵
self.Wih = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
self.Who = numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))
# 激活函数
self.activation_function = activation_function
pass
# 训练函数
def train(self, inputs_list, targets_list):
# 输入的目标list改为2D数组
targets = numpy.array(targets_list, ndmin=2).T
# 第一步计算结果(与query一致)
inputs = numpy.array(inputs_list, ndmin=2).T
hidden_inputs = numpy.dot(self.Wih, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
final_inputs = numpy.dot(self.Who, hidden_outputs)
final_outputs = self.activation_function(final_inputs)
# 计算输出层误差 error_output = 目标值 - 测量值
output_errors = targets - final_outputs
# 计算隐含层误差 errors_hidden = w_hidden2output^T errors_output
hidden_errors = numpy.dot(self.Who.T, output_errors)
服务器托管网# 权重更新
self.Who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)),
numpy.transpose(hidden_outputs))
self.Wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
numpy.transpose(inputs))
pass
# 查询函数
def query(self, inputs_list):
# 输入的list改为2D数组
inputs = numpy.array(inputs_list, ndmin=2).T
# 隐含层的输入 hidden_inputs = w_input2hedden inputs
hidden_inputs = numpy.dot(self.Wih, inputs)
# 隐含层的输出 hidden_outputs = sigmoid(hidden_inputs)
hidden_outputs = self.activation_function(hidden_inputs)
# 输出层的输入
final_inputs = numpy.dot(self.Who, hidden_outputs)
# 输出层的输出
final_outputs = self.activation_function(final_inputs)
return final_outputs
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: Unity打包Android,jar文件无法解析的问题
Unity打包Android,jar无法解析的问题 介绍 解决方案 总结 介绍 最近在接入语音的SDK时,发现的这个问题. 当我默认服务器托管网导入这个插件的时候,插件内部的文件夹(我下面话红框的文件夹)名字原本为GCloudVoice,这时候我默认打包的时候…