文章目录
前言
由于本人大一的时候只是粗略的通过一些实例去接触过深度学习,并没有系统的学习过,所以看到《深度学习入门——基于Python的理论与实践》这一本书的时候,觉得是一个较为基础的系统学习手册,所以决定从这本书开始,系统的学习深度学习。
博客主要用于自学时的记录和反思。
由于此书前面一些内容比较浅显,所以一些内容会被跳过记录
一、python的一些知识
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
1.基本知识
2.numpy
矩阵乘法(叉乘):np.dot(A,B)
二、感知机
感知机算是神经网络的基础
感知机主要提出的概念是偏置和权重。
{
0
b
+
w
1
x
1
+
w
2
x
2
≤
0
1
b
+
w
1
x
1
+
w
2
x
2
>
0
\left\{ {\begin{matrix}{} 0&{b + {w_1}{x_1} + {w_2}{x_2} \le 0}\\ 1&{b + {w_1}{x_1} + {w_2}{x_2} > 0} \end{matrix}} \right.
{01b+w1x1+w2x2≤0b+w1x1+w2x2>0
其中b是偏置,w是权重,x为输入
主要推理流程——当输入量乘以其权重大于某一个域值则为1,小于某一个域值则为0.当把域值降低为0,所以就相对为输入量乘以权重加上一个偏置是否大于零,大于零则为1,小于零则为0.
线性空间:由直线分割形成的空间
非线性空间:由曲线分割形成的空间
总结:
1.感知机时具有输入和输出的算法
2.感知机将权重和偏执设定为自己的参数
3.可以用感知机去表示与门和或门,但不可以用单层感知机去表示异或门(2层可以)
4.单层感知机可以表示线性空间,多层感知机可以表示非线性空间
5.多层感知机理论上可以表示计算机
三、神经网络
3.1.感知机到神经网络
a
=
w
1
x
1
+
w
2
x
2
+
b
a = {w_1}{x_1} + {w_2}{x_2} + b
a=w1x1+w2x2+b
y
=
h
(
a
)
y = h(a)
y=h(a)
其中,a为加权输入信号和偏置的总和,h为激活函数,y代表输出。整一个大圆代表一个神经元。
3.2.激活函数
像之前的,以零作为域值,超过为1,没超过为0,这样就相当于激活函数为阶跃函数。
3.2.1 阶跃函数
例如:
def step_function(x):
if x>0:
return 1
else:
return 0
3.2.2 sigmoid函数
h ( x ) = 1 1 + e − x h(x) = \frac{1}{{1 + {e^{ - x}}}} h(x)=1+e−x1
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
对比阶跃函数和sigmoid函数,要注意sigmoid的平滑性,对神经网络的学习有了重要的意义。
前面两种都算是线性函数,但由于不管如何加深层数,总是存在域值等效的“无隐藏层的神经网络”,为了发挥叠加层的优势,激活函数必须使用非线性函数。
3.2.3 ReLU函数
h ( x ) = θ { x ( x > 0 ) 0 ( x < = 0 ) h(x) = \theta \left\{ {\begin{matrix}{} x&{(x > 0)}\\ 0&{(x < = 0)} \end{matrix}} \right. h(x)=θ{x0(x>0)(x<=0)
import numpy as np
def relu(x):
return np.maximun(0,x)
这里使用了numpy里面的maximun,用于从输入数值中选择较大的那个值进行输出。
3.3.多层神经网络
用矩阵乘法的形式就可以将第1层的加权和表示成下面的式子
A
(
1
)
=
X
W
(
1
)
+
B
(
1
)
{A^{(1)}} = X{W^{(1)}} + {B^{(1)}}
A(1)=XW(1)+B(1)
其中
A
(
1
)
=
(
a
1
(
1
)
a
2
(
1
)
a
3
(
1
)
)
{A^{(1)}} = (\begin{matrix}{} {{a_1}^{(1)}}&{{a_2}^{(1)}}&{{a_3}^{(1)}} \end{matrix})
A(1)=(a1(1)a2(1)a3(1)) ,
X
=
(
x
1
x
2
)
X = (\begin{matrix}{} {{x_1}}&{{x_2}} \end{matrix})
X=(x1x2),
B
(
1
)
=
(
b
1
(
1
)
b
2
(
1
)
b
3
(
1
)
)
{B^{(1)}} = (\begin{matrix}{} {{b_1}^{(1)}}&{{b_2}^{(1)}}&{{b_3}^{(1)}} \end{matrix})
B(1)=(b1(1)b2(1)b3(1)),
W
(
1
)
=
(
w
11
(
1
)
w
21
(
1
)
w
31
(
1
)
w
12
(
1
)
w
22
(
1
)
w
32
(
1
)
)
{W^{(1)}} = (\begin{matrix}{} {{w_{11}}^{(1)}}&{{w_{21}}^{(1)}}&{{w_{31}}^{(1)}}\\ {{w_{12}}^{(1)}}&{{w_{22}}^{(1)}}&{{w_{32}}^{(1)}} \end{matrix})
W(1)=(w11(1)w12(1)w21(1)w22(1)w31(1)w32(1))
前向传播:从输入到输出方向的传递。
本章所有代码块的总结:已知各层的w和b可以前向传播得到y。
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
def identity_function(x):
return x
def init_network():
network={}
network['w1']=np.array([[0.1,0.3,0.5],[0.2,0.3,0.6]])
network['b1']=np.array([0.1,0.2,0.3])
network['w2']=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network['b2']=np.array([0.1,0.2])
network['w3']=np.array([[0.1,0.3],[0.2,0.4]])
network['b3'] = np.array([0.1,0.2])
return network
def forward(network,x):
w1,w2,w3=network['w1'],network['w2'],network['w3']
b1,b2,b3=network['b1'],network['b2'],network['b3']
a1=np.dot(x,w1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,w2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,w3)+b3
y=identity_function(a3)
return y
network=init_network()
x=np.array([1.0,0.5])
y=forward(network,x)
print(y)
3.4.恒等函数和softmax函数
恒等函数:输出等于输出。(相当于上面代码段的identity_funtion)
softmax函数:用于分类问题
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
{y_k} = \frac{{\exp ({a_k})}}{{\sum\limits_{i = 1}^n {\exp ({a_i})} }}
yk=i=1∑nexp(ai)exp(ak)
但是分母有时候过大(因为是指数的总和,所以可能会导致inf),所以我们运用数学知识对上面式子可以适当的恒等变换。
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
=
exp
(
a
k
+
C
′
)
∑
i
=
1
n
exp
(
a
i
+
C
′
)
{y_k} = \frac{{\exp ({a_k})}}{{\sum\limits_{i = 1}^n {\exp ({a_i})} }} = \frac{{\exp ({a_k} + {C'})}}{{\sum\limits_{i = 1}^n {\exp ({a_i} + {C'})} }}
yk=i=1∑nexp(ai)exp(ak)=i=1∑nexp(ai+C′)exp(ak+C′)
这里C‘可以用任何值来防止其溢出。
我们通过式子也可以得知,输出总和为1是softmax函数的一个重要性质,所以可以将其引申为“概率”
四、神经网络的学习
本章思路:我们可以得知,前面所学的都是已知w和b然后得知输出。但神经网络最重要的是自学习过程,也就是通过已知样本不断调整其参数达到较优,即自动获得最优权重参数的过程。而为了能将最优秀权重参数量化,我们引入了损失函数,即找出能使损失函数最小的权重参数。为了能找到最小,我们利用了函数斜率梯度法。
4.1一些基本的概念
机器学习中,一般将数据分成训练数据和测试数据。首先利用训练数据(又称“监督数据”)进行学习寻找最优参数;然后使用测试数据评价训练得到的模型的实际能力。
泛化能力:处理未被观察过的数据(即不包含在训练数据中的数据),获得泛化能力是机器学习的最终目标。
注意,仅仅用一个数据集去学习和评价参数是无法进行正确评价的,这样可能导致这种学习后的能力无法迁移到其他数据集上去。这就引出了过拟合的概念。
过拟合:只对某个数据集过度拟合的状态。
4.2 损失函数
损失函数表示神经网络性能的“恶劣程度的指标”。一般是用均方误差和交叉熵误差等。
4.2.1 均方误差
E
=
1
2
∑
k
(
y
k
−
t
k
)
2
E = \frac{1}{2}\sum\limits_k {{{({y_k} - {t_k})}^2}}
E=21k∑(yk−tk)2
其中,
E
{E}
E针对单个数据的损失函数,
y
k
{y_k}
yk表示神经网络的输出,
t
k
{t_k}
tk表示监督数据,k表示数据的维数。
4.2.2 交叉熵误差
E
=
−
∑
k
t
k
log
y
k
E = - \sum\limits_k {{t_k}\log {y_k}}
E=−k∑tklogyk
其中,
E
{E}
E针对单个数据的损失函数,
y
k
{y_k}
yk表示神经网络的输出,
t
k
{t_k}
tk表示正确解标签,只有正确解标签处的值才为1,其他都为0.
4.3 mini-batch学习
我们需要把所有训练数据的损失函数的总和作为学习目标
以交叉熵误差为例:
E
=
−
1
N
∑
n
∑
k
t
n
k
log
y
n
k
{\rm{E}} = - \frac{1}{N}\sum\limits_n {\sum\limits_k {{t_{nk}}\log {y_{nk}}} }
E=−N1n∑k∑tnklogynk
但是以全部数据为对象计算损失函数是不现实的。所以实际神经网络的学习也是从训练数据中选出一批数据(称为min-batch,小批量),然后对每个mini-batch进行学习。
所以mini-batch类似于利用一部分样本数据来近似计算整体。
例如:
MNIST数据集,从60000个训练数据中随机选100笔进行学习
import numpy as np
train_size=x_train.shape[0]
batch_size=10
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
利用np.random.choice()可以从指定中随机选择
4.4 数值微分
4.4.1 梯度法
目标:神经网络在学习时找到损失函数最小值时的参数(权重和偏置)。
(
∂
f
∂
x
0
∂
f
∂
x
1
)
(\begin{matrix}{} {\frac{{\partial f}}{{\partial {x_0}}}}&{\frac{{\partial f}}{{\partial {x_1}}}} \end{matrix})
(∂x0∂f∂x1∂f)
梯度
:各点处的函数值减小最多的方向。(所以无法保证态度所指向的方向就是函数的最小值或者真正应该前进的方向)(所以绝大多数可能陷入局部最优解)
用代码实现梯度
def numercial_gradient(f,x):
h=1e-4#步长
grad=np.zeros_like(x)#生成和x形状相同的数组
for idx in range(x.size):
tmp_val=x[idx]
#计算f(x+h)
x[idx]=tmp_val+h
fxh1=f(x)
# 计算f(x-h)
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx]=(fxh1-fxh2)/(2*h)
x[idx]=tmp_val #还原值
return grad
梯度法
:函数的取值从当前位置沿着梯度方向前进一定距离,然后再新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断沿着梯度方向前进。(可以想想小球在一块凹凸不平的雪山上向下滑行,然后到达最低点。)
如果用数学表达式表达:
x
0
=
x
0
−
η
∂
f
∂
x
0
{x_0} = {x_0} - \eta \frac{{\partial f}}{{\partial {x_0}}}
x0=x0−η∂x0∂f
其中:
η
\eta
η称为学习率,其决定这一次学习中,应该学习多少,以及在多大程度上更新参数。
学习率要先确定一个值,然后不断地调整,找到一个较好的学习率。
梯度下降法的代码实现:
def gradient_descent(f,ini_x,lr=0.01,step_num=100):
x=init_x
for i in range(step_num):
grad=numerical_gradient(f,x)#求函数的梯度
x-=lr*grad
return x
f f f 是要进行优化的参数,step_num是指梯度重复的次数。
4.4.1 随机梯度下降法代码实现
步骤
1.选出mini-batch
2.计算梯度
3.更新参数
4.重复123
随机梯度下降法:对随机选择的数据经先梯度下降法。
接下来我们用2层神经网络训练MNIST数据集
此命名为 two_layer_net.py
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient
class TwoLayerNet:
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 初始化权重
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
def predict(self, x):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
# x:输入数据, t:监督数据
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
# x:输入数据, t:监督数据
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
return grads
# 计算权重参数的梯度,是 numerical_gradient 的高速版
def gradient(self, x, t):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
grads = {}
batch_num = x.shape[0]
# forward
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
# backward
dy = (y - t) / batch_num
grads['W2'] = np.dot(z1.T, dy)
grads['b2'] = np.sum(dy, axis=0)
da1 = np.dot(dy, W2.T)
dz1 = sigmoid_grad(a1) * da1
grads['W1'] = np.dot(x.T, dz1)
grads['b1'] = np.sum(dz1, axis=0)
return grads
变量 | 说明 |
---|---|
params | 保存神经网络参数的字典型变量 |
grads | 保存梯度的字典型变量方法的返回值 |
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
iters_num = 10000 # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
train_loss_list = []
train_acc_list = []
test_acc_list = []
iter_per_epoch = max(train_size / batch_size, 1)
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# 计算梯度
#grad = network.numerical_gradient(x_batch, t_batch)
grad = network.gradient(x_batch, t_batch)
# 更新参数
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))
# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
epoch:一个单位,一个epoch表示学习中所有训练数据均被使用过一次时更新次数。(实际操作时,mini-batch是一开始被打乱,然后按序号遍历)
总结
提示:这里对文章进行总结:
1-4章大致更新完成,但略微仓促,来日有空再修正。第5-8章将在另一篇文章中呈现。
感谢。