【笔记】深度学习入门:基于Python的理论与实现(一)

《深度学习入门:基于Python的理论与实现》看了下,这本书对我这种没有任何深度学习基础的小白很有用也很友好,而且是本需要反复阅读加深记忆的读本,故将重要内容整理记录,方便后续快速回顾复习,与大家共勉~

感知机

感知机接收多个输入信号,输出一个信号。和实际的电 流不同的是,感知机的信号只有“流 / 不流”(1/0)两种取值。0 对应“不传递信号”,1 对应“传递信号”。

感知机的定义

图是一个接收两个输入信号的感知机的例子。x1、x2 是输入信号, y 是输出信号,w1、w2 是权重(若 b 为 −0.1,则只要输入信号的加权总和超过 0.1,神经元就会被激活。但是如果 b 为 −20.0,则输入信号的加权总和必须超过 20.0,神经元才会被激活。像这样, 偏置的值决定了神经元被激活的容易程度)。图中的○称为“神 经元”或者“节点”。输入信号被送往神经元时,会被分别乘以固定的权重
在这里插入图片描述
(w1x1、w2x2)。神经元会计算传送过来的信号的总和,只有当这个总和超过 了某个界限值时,才会输出1。这也称为“神经元被激活”。这里将这个界 限值称为阈值,用符号 θ 表示。用数学式表达就是
在这里插入图片描述

感知机的实现

简单实现

简单的实现“与”门的实现

# 在函数内初始化参数 w1、w2、theta,当输入的加权总和超过阈值时返回 1,
否则返回 0
def AND(x1, x2):
	w1, w2, theta = 0.5, 0.5, 0.7 
	tmp = x1*w1 + x2*w2
	if tmp <= theta:
		return 0
	elif tmp > theta:
		return 1

导入权重和偏置

b 称为偏置,w1 和 w2 称为权重(偏置是调 整神经元被激活的容易程度(输出信号为 1 的程度)的参数)
在这里插入图片描述

式子对应的计算如下:

>>> import numpy as np
>>> x = np.array([0, 1]) # 输入
>>> w = np.array([0.5, 0.5]) # 权重
>>> b = -0.7 # 偏置
>>> w*x # 两个数组的元素对应相乘
array([0. , 0.5])
>>> np.sum(w*x) # 各元素之和
0.5
>>> np.sum(w*x) + b 
-0.19999999999999996# 大约为-0.2(由浮点小数造成的运算误差)

利用此来实现与门、与非门、或门(与门、与非门、或门是具有相同构造的感知机, 区别只在于权重参数的值):

# 与门
def AND(x1, x2):
	x = np.array([x1, x2])
	w = np.array([0.5, 0.5]) 
	b = -0.7
	tmp = np.sum(w*x) + b
	if tmp <= 0:
		return 0 
	else:
		return 1

# 与非门
def NAND(x1, x2):
	x = np.array([x1, x2])
	w = np.array([-0.5, -0.5]) # 仅权重和偏置与AND不同! 
	b = 0.7
	tmp = np.sum(w*x) + b
	if tmp <= 0:
		return 0 
	else:
		return 1 

# 或门
def OR(x1, x2):
	x = np.array([x1, x2])
	w = np.array([0.5, 0.5]) # 仅权重和偏置与AND不同! 
	b = -0.2
	tmp = np.sum(w*x) + b
	if tmp <= 0:
		return 0 
	else:
		return 1

感知机的局限性

以异或门为例(仅当 x1 或 x2 中的一方为 1 时 , 才 会 输 出 1 ),前面介绍的感知机是无法实现这个异或门的
在这里插入图片描述在这里插入图片描述
一条直线无法完全分开0和1的情况(非线性的):
在这里插入图片描述
或门是线性的:
在这里插入图片描述

多层感知机

电路组合

感知机 的绝妙之处在于它可以“叠加层”。从而去解决无法实现异或门的问题。

简单门电路的表示
在这里插入图片描述
通过简单门实现异或门
在这里插入图片描述

异或门的实现

python逻辑实现:

def XOR(x1, x2):
	s1 = NAND(x1, x2) 
	s2 = OR(x1, x2)
	y = AND(s1, s2) 
	return y

使用感知机实现:
在这里插入图片描述

小结

• 感知机是具有输入和输出的算法。给定一个输入后,将输出一个既定的值。
• 感知机将权重和偏置设定为参数。
• 使用感知机可以表示与门和或门等逻辑电路。
• 异或门无法通过单层感知机来表示。
• 使用2层感知机可以表示异或门。
• 单层感知机只能表示线性空间,而多层感知机可以表示非线性空间。
• 多层感知机(在理论上)可以表示计算机。

神经网络

神经网络的一 个重要性质是它可以自动地从数据中学习到合适的权重参数,不再需要人工配置

感知机到神经网络

中间层有时也称为隐藏层。
在这里插入图片描述

简单激活函数

在这里插入图片描述
在这里插入图片描述
h(x)函数会将输入信号的总和转换为输出信号,这种函数 一般称为激活函数,也可以表达成以下两个式子

a = b + w1x1 + w2x2
y = h(a)

一 般而言,“朴素感知机”是指单层网络,指的是激活函数使用了阶跃函数(是指一旦输入超过阈值,就切换输出的函数) A 的模型。“多层感知机”是指神经网络,即使用 sigmoid 函数(后述)等平滑的激活函数的多层网络。

激活函数

sigmoid函数

常使用的一个激活函数表达式( exp(−x) 表示 e−x次方 ):
在这里插入图片描述

阶跃函数的图形

可以用python实现为:

import numpy as np
import matplotlib.pylab as plt
def step_function(x):
	return np.array(x > 0, dtype=np.int) # x>0会对数组的各项不等式判定,结果为True和False,dtype=np.int会把结果转换为1、0
x = np.arange(-5.0, 5.0, 0.1) # 在−5.0到5.0的范围内,以0.1为单位,生成 NumPy数组
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围 plt.show()

结果图形:
在这里插入图片描述

sigmoid 函数的实现

把阶跃函数的step_functon()改成sigmoid即可

阶跃函数与sigmoid函数的对比

在这里插入图片描述

两者均为非线性函数

ReLU 函数

sigmoid 函数很早就开始被使用了,而最近则主要 使用ReLU(Rectified Linear Unit)函数。ReLU 函数在输入大于 0 时,直接输出该值;在输入小于等于0时,输出0
在这里插入图片描述

def relu(x):
	return np.maximum(0, x)

多维数组

多位数组的生成常用函数在使用时再解释,矩阵的简单运算此处略过

神经网络的内积

在这里插入图片描述

>>> X = np.array([1, 2])
>>> X.shape # shape打印数组形状
(2,)
>>> W = np.array([[1, 3, 5], [2, 4, 6]]) 
>>> print(W)
[[1 3 5] [2 4 6]]
>>> W.shape
(2, 3)
>>> Y = np.dot(X, W) # dot 数组乘积(矩阵的乘积,看不懂的话学一下矩阵的运算)
>>> print(Y)
[ 5 11 17]

三层神经网络的实现

在这里插入图片描述
权重的符号说明:
在这里插入图片描述

这样可以使用公式表示(a是后一层的神经元,w是权重,x是输入,b是偏置)

在这里插入图片描述

采用矩阵的乘法表示如下:

在这里插入图片描述

python使用NumPy多维数组实现如下:

def init_network(): 
	network = {}
	network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 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)# 或者Y = A3(这个函数是恒等函数,输出层的激活函数用 σ() 表示,不同于隐藏层的激活函数 h(),这里σ() 就是这个恒等函数,只是为了和其他层的传递流程保持一致)
	return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [ 0.31682708 0.69627909]

用图表信号传递过程(第0层至第一层,后两层以此类推)

在这里插入图片描述

输出层

神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出 层的激活函数。一般而言,回归问题用恒等函数,分类问题用 softmax 函数。

softmax 函数

分母是所有输入信号的指数函数的和,可以看出, 输出层的各个神经元都受到所有输入信号的影响,输出总和为 1 是 softmax 函数的一个重要性质,正因为有了这个性质,我们才可以把 softmax 函数的输出解释为“概率”。
在这里插入图片描述

使用python定义softmax函数:

def softmax(a):
	exp_a = np.exp(a) 
	sum_exp_a = np.sum(exp_a) 
	y = exp_a / sum_exp_a
	return y

但是这样实现有指数函数做分子,所以可能存在溢出问题导致结果不准确,所以进行改进:
在这里插入图片描述

推导式子说明,在进行 softmax 的指数函数的运算时,加上(或者减去) 某个常数并不会改变运算的结果。这里的 C‘可以使用任何值,但是为了防止溢出,一般会使用输入信号中的最大值使用python定义softmax函数

使用python定义softmax函数:

def softmax(a):
	c = np.max(a)
	exp_a = np.exp(a - c) # 溢出对策 
	sum_exp_a = np.sum(exp_a)
	y = exp_a / sum_exp_a
	return y

特征:

>>> a = np.array([0.3, 2.9, 4.0])
>>> y = softmax(a)
>>> print(y)
[ 0.01821127 0.24519181 0.73659691] 
>>> np.sum(y)
1.0

上面的例子可以解释成 y[0] 的概率是 0.018(1.8 %),y[1] 的概率 是 0.245(24.5 %),y[2] 的概率是0.737(73.7 %)。从概率的结果来看,可以说“因为第2个元素的概率最高,所以答案是第2个类别”。而且,还可以回答“有74%的概率是第2个类别,有25%的概率是第1个类别,有1%的概率是第0个类别”。也就是说,通过使用 softmax 函数,我们可以用概率的(统计的)方法处理问题。

输出层的神经元数量

对于分类问题,输 出层的神经元数量一般设定为类别的数量。比如,对于某个输入图像,预测 是图中的数字 0 到 9 中的哪一个的问题(10 类别分类问题),可以像图 3-23 这样, 将输出层的神经元设定为 10 个。

手写数字识别

和求解机器学习问题的步骤(分成学习和推理两个阶段进行)一样, 使用神经网络解决问题时,也需要首先使用训练数据(学习数据)进 行权重参数的学习;进行推理时,使用刚才学习到的参数,对输入数据进行分类。

MNIST 数据集

MNIST 数据集是由 0 到 9 的数字图像构成的(图 3-24)。训练图像有 6 万张, 测试图像有 1 万张,这些图像可以用于学习和推理。MNIST 数据集的一般 使用方法是,先用训练图像进行学习,再用学习到的模型度量能在多大程度上对测试图像进行正确的分类。MNIST的图像数据是28像素 ×28像素的灰度图像(1通道),各个像素 的取值在 0 到 255 之间

  • 读入minist数据
import sys, os
sys.path.append(os.pardir) # 为了导入父目录中的文件而进行的设定 
from dataset.mnist import load_mnist
# 第一次调用会花费几分钟......
# load_mnist 函数以“( 训练图像 , 训练标签 ),( 测试图像,测试标签 )”的 形式返回读入的 MNIST 数据
# 第 1 个参数normalize 设置是否将输入图像正规化为 0.0~1.0 的值。如果将该参数设置 为 False,则输入图像的像素会保持原来的 0~255。
# 第 2 个参数 flatten 设置是否展开输入图像(变成一维数组)。如果将该参数设置为False,则输入图像为 1 × 28 × 28 的三维数组;若设置为 True,则输入图像会保存为由 784 个元素构成的一维数组。
# 第 3 个参数 one_hot_label 设置是否将标签保存为 one-hot 表示(one-hot representation)。one-hot 表示是仅正确解标签为 1,其余皆为0的数组,就像 [0,0,1,0,0,0,0,0,0,0] 这样。当 one_hot_label 为 False 时, 只是像 7、2 这样简单保存正确解标签;当one_hot_label 为 True 时,标签则保存为 one-hot 表示。
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
# 输出各个数据的形状 
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,) 
print(x_test.shape) # (10000, 784) 
print(t_test.shape) # (10000,)
  • 显示 MNIST 图像
import sys, os sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist from PIL import Image
def img_show(img):
	pil_img = Image.fromarray(np.uint8(img)) 
	pil_img.show()
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784,)
img = img.reshape(28, 28) # 把图像的形状变成原来的尺寸 
print(img.shape) # (28, 28)
img_show(img)

神经网络的推理处理


def get_data():
	(x_train, t_train), (x_test, t_test) = \
		load_mnist(normalize=True, flatten=True, one_hot_label=False) 
	return x_test, t_test
# 读入保存在 pickle 文件 sample_weight.pkl 中的学习到的 权重参数(假设学习已经完成)。
def init_network():
	with open("sample_weight.pkl", 'rb') as f:
		network = pickle.load(f) 
		return network
def predict(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 = softmax(a3)
	return y

# 实现
x, t = get_data() 
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
	y = predict(network, x[i]) # 获取对各个数字的概率
	p = np.argmax(y) # 获取概率最高的元素的索引 
	if p == t[i]:# 如果和答案相同
		accuracy_cnt += 1
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

将图像的各个像 素值除以 255,使得数据的值在 0.0~1.0 的范围内。像这样把数据限定到某 个范围内的处理称为正规化(normalization)

批处理优化:

x, t = get_data() network = init_network()
batch_size = 100 # 批数量 
accuracy_cnt = 0
for i in range(0, len(x), batch_size): # batch_size是自增的步数
	x_batch = x[i:i+batch_size]
	y_batch = predict(network, x_batch)
	p = np.argmax(y_batch, axis=1) # 指定第一维度的比较,也就是依次比较各个数组的第一个数
	accuracy_cnt += np.sum(p == t[i:i+batch_size])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

小结

• 神经网络中的激活函数使用平滑变化的sigmoid函数或ReLU函数。
• 通过巧妙地使用NumPy多维数组,可以高效地实现神经网络。
• 机器学习的问题大体上可以分为回归问题和分类问题。
• 关于输出层的激活函数,回归问题中一般用恒等函数,分类问题中
一般用 softmax 函数。
• 分类问题中,输出层的神经元的数量设置为要分类的类别数。
• 输入数据的集合称为批。通过以批为单位进行推理处理,能够实现
高速的运算。

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值