深度学习入门笔记
- 感知机
- 逻辑与门
- 与非门
- 或门
- 多层感知机
- 异或门
- 神经网络
- 激活函数
- 输出层设计
- 损失函数
- 均方误差 MSE
- 交叉熵误差
- 反向传播算法
- 计算图
- 局部计算
- 计算图反向传播
- 反向传播
- 参数更新
- 训练过程
- 总结
该篇文章为本人学习笔记的一部分。笔记基于《深度学习入门 基于python理论实践》这本书学习,尽可能简单的分享吧。
感知机
接收多个信号,输出一个信号。
x1,x2是输入信号,w1,w2是权重,y是输出信号。信号送往神经元时,都会乘以相对应的权重(x1w1, x2w2)。神经元计算传来的信号总和,当总和超过一定阈值则输出1,否则输出0。
y
=
{
0
,
(
w
1
x
1
+
w
2
x
2
≤
θ
)
1
,
(
w
1
x
1
+
w
2
x
2
>
θ
)
y = \begin{cases} 0, & {(w_1x_1 + w_2x_2 \leq \theta)}\\\\ 1, & {(w_1x_1 + w_2x_2 \gt \theta)} \end{cases}
y=⎩
⎨
⎧0,1,(w1x1+w2x2≤θ)(w1x1+w2x2>θ)
逻辑与门
通过感知机来表示 逻辑与门。也就是说需要找到(w1,w2,θ) 相应的权重和偏置,满足条件。如:(0.5,0.5,0.7)满足条件,即 (0.5 * x1 + 0.5 * x2 <= 0.7 时为0, > 0.7时为1)当然(0.5,0.5,0.8)也可以。
import numpy as np
def AND(x1,x2):
w = np.array([0.5,0.5,-0.7])
x = np.array([x1,x2,1])
temp = np.sum(w*x)
if temp <= 0:
return 0
else:
return 1
与非门
和与门相反
import numpy as np
def NAND(x1,x2):
w = np.array([-0.5,-0.5,0.7])
x = np.array([x1,x2,1])
temp = np.sum(w*x)
if temp <= 0:
return 0
else:
return 1
或门
import numpy as np
#或门
def OR(x1,x2):
w = np.array([1.0,1.0,-0.5])
x = np.array([x1,x2,1])
temp = np.sum(w*x)
if temp <= 0:
return 0
else:
return 1
将x1,x2,作为横竖坐标,并将(0,0),(1,0),(0,1),(1,1)点画到坐标中,每个点Y如果是0,画○,如果是1,画×. 不难得出权重。
多层感知机
异或门
那么异或门如何表示。
仅当x1,x2当中的一方为1时才会输出1。
可以通过或门,与非门加上与门去完成。
def XOR(x1,x2):
s1 = NAND(x1,x2)
s2 = OR(x1,x2)
y = AND(s1,s2)
return y
神经网络
神经网络和感知机相似,最大区别就在于激活函数。
激活函数
常见激活函数有Sigmoid,ReLu,LeakyRelu等等。
感知机函数形式
h
(
x
)
=
{
0
,
(
w
1
x
1
+
w
2
x
2
≤
θ
)
1
,
(
w
1
x
1
+
w
2
x
2
>
θ
)
h(x) = \begin {cases} 0, &{(w_1x_1 + w_2x_2 \leq \theta)} \\\\ 1, &{(w_1x_1 + w_2x_2 \gt \theta)} \end {cases}
h(x)=⎩
⎨
⎧0,1,(w1x1+w2x2≤θ)(w1x1+w2x2>θ)
变形
h
(
x
)
=
{
0
,
(
w
1
x
1
+
w
2
x
2
+
b
≤
0
)
1
,
(
w
1
x
1
+
w
2
x
2
+
b
>
0
)
h(x) = \begin {cases} 0, &{(w_1x_1 + w_2x_2 + b \leq 0)} \\\\ 1, &{(w_1x_1 + w_2x_2 + b \gt 0)} \end {cases}
h(x)=⎩
⎨
⎧0,1,(w1x1+w2x2+b≤0)(w1x1+w2x2+b>0)
激活函数就是前面的感知机的简化变形
h
(
x
)
=
{
0
,
(
x
≤
0
)
1
,
(
x
>
0
)
h(x) = \begin {cases} 0, &{(x \leq 0)} \\\\ 1, &{(x\gt0)} \end {cases}
h(x)=⎩
⎨
⎧0,1,(x≤0)(x>0)
此处h(x)就是激活函数。
sigmoid
h
(
x
)
=
1
1
+
e
(
−
x
)
h(x) = \frac {1}{1 + e^{(-x)}}
h(x)=1+e(−x)1
阶跃函数
就是一个阈值,超过就是1,没超过就是0
ReLu
h
(
x
)
=
{
x
,
(
x
>
0
)
0
,
(
x
≤
0
)
h(x) = \begin {cases}x, &{(x \gt 0)}\\\\ 0, &{(x \leq 0)} \end{cases}
h(x)=⎩
⎨
⎧x,0,(x>0)(x≤0)
为什么要有激活函数:激活函数都是非线性的,如果是线性函数,h(x) = c * x,叠加三层就成了c * c * c * x 相当于 a * x将毫无意义。
输出层设计
神经网络可分为两类问题:分类问题,回归问题
分类问题:属于哪一类
回归问题:预测数值
恒等函数:数值原样输出,用于回归问题。
Softmax函数:用于分类问题
y
k
=
e
a
k
∑
i
=
1
n
e
(
a
i
)
y_k = \frac {e^{a_k}}{\sum_{i=1}^{n}e^{(a_i)}}
yk=∑i=1ne(ai)eak
softmax可以让输出的值总和为1,可以理解为softmax的输出解释为“概率”
输出神经元数量: 按照类别数量设定。
损失函数
表示神经网络性能的“恶劣程度”的指标,当前神经网络对监督数据在大多程度上不拟合,大多程度不一致。
均方误差 MSE
E
=
1
2
∑
k
(
y
k
−
t
k
)
2
E = \frac {1}{2} \sum_k (y_k - t_k)^2
E=21k∑(yk−tk)2
yk : 神经网络的输出
tk : 监督数据
k : 数据维数
交叉熵误差
E
=
−
∑
k
t
k
l
o
g
(
y
k
)
E = -\sum_k t_klog(y_k)
E=−k∑tklog(yk)
小例子,MNIST手写数据集训练图像识别,MNIST数据集6w训练样本,1w测试样本,每张图28 x 28像素。标签为one_hot_label (一个大小为10的数组,图像的数字对应数组位置上的元素为1,其余为0)。训练中采用的交叉熵误差。详细代码见我博客地址
反向传播算法
这部分算是比较重要的,权重的更新依靠这个方法。
书上的例题:太郎在超市买了2个100日元一个的苹果,消费税是10%,请计算支付金额。
很简单的计算:100 * 2 * 1.1即可。
将x2和x1.1节点中的数字取出,符号单独放在○当中。
从左往右为正向传播
从右往左是反向传播
计算图
计算图:就是将计算过程用图的方式存下来。
局部计算
简单来说就是只用关注当前的简单计算部分,其他复杂的部分不需要管。意思就是计算偏导的那种感觉。
计算图反向传播
假设y = f(x)就是将信号E乘以节点的局部导数,然后传递到下一个节点。如果假设y = x^2 那么导数为2x,那么向下传播的值就是 E*2x,这里的x是正向传递时记录的。
链式法则
f
(
x
,
y
)
=
(
x
+
y
)
2
令
u
=
(
x
+
y
)
∂
f
∂
x
=
∂
f
∂
u
∂
u
∂
x
=
2
(
x
+
y
)
∂
f
∂
y
=
∂
f
∂
u
∂
u
∂
y
=
2
(
x
+
y
)
f(x,y) = (x + y)^2 \\ 令u = (x + y) \\ \frac{\partial f}{\partial x} = \frac{\partial f}{\partial u} \frac{\partial u}{\partial x} = 2(x + y) \\ \\ \frac{\partial f}{\partial y} = \frac{\partial f}{\partial u} \frac{\partial u}{\partial y} = 2(x + y) \\
f(x,y)=(x+y)2令u=(x+y)∂x∂f=∂u∂f∂x∂u=2(x+y)∂y∂f=∂u∂f∂y∂u=2(x+y)
反向传播
对于每个层,都有forward方法和backward方法,对应正向反向传播。在训练时创建网络时,将每一层存在一个列表当中,顺序正向传播。当需要计算梯度时,将列表翻转,依次执行backward方法进行反向传播,求得梯度。
加法正向和反向示意图
乘法示意图
回到苹果的问题,计算过程反向传播过程。
参数更新
SGD随机梯度下降算法举例。其他更新的算法有很多Adam,Momentum,AdaGrad等等。
W
′
=
W
−
η
∇
f
(
W
)
W' = W - \eta \nabla f(W)
W′=W−η∇f(W)
W
W
W为旧的权重,
W
′
W'
W′更新的权重,
η
\eta
η学习率,
∇
f
(
W
)
\nabla f(W)
∇f(W)为损失函数在W权重上的导数。
训练过程
以MNIST数据集为例,简单写个训练过程伪代码。用pytorch或TensorFlow会更方便些。
# 加载数据集
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True)
# 创建网络
network = MultiLayerNet(input_size=784,hidden_size_list=[100,100,100,100,100,100],output_size=10)
# 优化器
optimizer = SGD(network.parameters(), lr = 0.01)
# 损失函数
loss_func = MSE()
# 参数设置
max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100
# 训练轮数
for i in range(max_epochs):
# 随机选择训练样本
batch_mask = np.random.choice(train_size,batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
optimizer.zero_grad() # 梯度参数归零
out = network(x_batch) # 正向传播
loss = loss_fun(out, t_batch) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 梯度参数更新
if(best > loss.item()):
best = loss.item()
save_network(network) # 指标更好,保存当前网络
本文章只是简单介绍一些基础学习笔记,一些优化的trick并没有讲。比如BatchNormization,dropout,过拟合的处理等等。
总结
个人感觉其实神经网络其实就是一个万能的拟合器,训练神经网络的过程,就是让计算机自己从大量的数据中寻找一条公式。这些数据有输入,输出结果,就是中间的函数映射是未知的,寻找的就是这些未知的映射。我们创建的神经网络,就是假定了函数的很多参数,通过损失函数,丈量与实际真实结果之间的距离。目标就是缩小之间的距离,而这个过程就相当于寻求一个函数的最低点,最低点的特征是导数为0,因此反向传播求导是获取了探寻最低点的方向,学习率就是前进的步长。然而最低点其实也是局部最优,为了更好的找到全局最优,也有很多的优化策略,比如余弦退火。
这里只介绍了一些基础算法过程,到后面的卷积神经网络原理也是一样的。