目录
1神经网络初解
2激活函数及实现
2.1初识激活函数
2.1激活函数类型及实现
2.1.1阶跃函数及实现
2.1.2sigmoid函数及实现
2.1.3Relu函数及实现
2.1.4恒等函数和softmax函数及实现
3手写数字识别
3.1mnist数据集
3.1.1下载数据集
3.1.2读取数据集
3.2神经网络推理处理
3.2.1ch03/neuralnet_mnist.py
3.2.2dataset/mnist.py
3.2.3common/active.py
3.3批处理
该文章是对《深度学习入门 基于Python的理论与实现》的总结,作者是[日]斋藤康毅
如图所示。我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层。中间层有时也称为隐藏层。
第0层对应输入层,第1层对应中间层,第2层对应输出层。本书将根据实质上拥有权重的层数(输入层、隐藏层、输出层的总数减去1后的数量)来表示网络的名称。
使用h(x)函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数(activation function)。先计算输入信号的加权总和,然后用激活函数转换这一总和,激活函数是连接感知机和神经网络的桥梁。
可以用下面的图来表示
由上图:信号的加权总和为节点a,然后节点a被激活函数h()转换成节点y。
【注】:“朴素感知机”是指单层网络,指的是激活函数使用了阶跃函数的模型。
【注】:“多层感知机”是指神经网络,即使用sigmoid函数等平滑的激活函数的多层网络。
激活函数以阈值为界,一旦输入超过阈值,就切换输出。这样的函数称为“阶跃函数”。在激活函数的众多候选函数中,感知机使用了阶跃函数。
图像如下图:
代码如下:当输入超过0时,输出1,否则输出0。
import numpy as np
import matplotlib.pylab as plt
def step_function1(x):
if x > 0:
return 1
else:
return 0
def step_function2(x):
y = x > 0
return y.astype(np.int)
def step_function(x):
return np.array(x > 0, dtype=np.int)
if __name__ == "__main__":
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
代码如下:
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
return 1/(1 + np.exp(-x))
if __name__ == "__main__":
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.title("sigmoid")
plt.xlabel("x")
plt.ylabel("y")
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
得到的图像如下
阶跃函数和sigmoid函数还有其他共同点,就是两者均为非线性函数。sigmoid函数是一条曲线,阶跃函数是一条像阶梯一样的折线,两者都属于非线性的函数。
线性函数是一条笔直的直线。而非线性函数,顾名思义,指的是不像线性函数那样呈现出一条直线的函数。
神经网络的激活函数必须使用非线性函数。换句话说,激活函数不能使用线性函数。为什么不能使用线性函数呢?因为使用线性函数的话,加深神经网络的层数就没有意义了。
为了发挥叠加层所带来的优势,激活函数必须使用非线性函数。
代码实现如下:
import numpy as np
import matplotlib.pylab as plt
def relu(x):
return np.maximum(0, x)
if __name__ == "__main__":
x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.title("Relu")
plt.xlabel("x")
plt.ylabel("y")
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。因此,在输出层使用恒等函数时,输入信号会原封不动地被输出。另外,将恒等函数的处理过程用之前的神经网络图来表示的话,则如图3-21所示。和前面介绍的隐藏层的激活函数一样,恒等函数进行的转换处理可以用一根箭头来表示。
恒等函数:
softmax函数改进:
【注】:在计算机的运算上有一定的缺陷。这个缺陷就是溢出问题。softmax函数的实现中要进行指数函数的运算,但是此时指数函数的值很容易变得非常大。比如,e 10 的值会超过20000,e 100 会变成一个后面有40多个0的超大值,e 1000 的结果会返回一个表示无穷大的 inf 。如果在这些超大值之间进行除法运算,结果会出现“不确定”的情况。
【注】:上式在分子和分母上都乘上C这个任意的常数(因为同时对分母和分子乘以相同的常数,所以计算结果不变)。然后,把这个C移动到指数函数(exp)中,记为log C。最后,把log C替换为另一个符号C 。在进行softmax的指数函数的运算时,加上(或者减去)某个常数并不会改变运算的结果。这里的C可以使用任何值,但是为了防止溢出,一般会使用输入信号中的最大值。
假设输出层共有n个神经元,计算第k个神经元的输出y k ,softmax函数的分子是输入信号a k 的指数函数,分母是所有输入信号的指数函数的和。softmax函数的输出通过箭头与所有的输入信号相连。这是因为,从上式可以看出,输出层的各个神经元都受到所有输入信号的影响。
代码如下:
import numpy as np
import matplotlib.pylab as plt
def softmax(x):
c = np.max(x)
exp_a = np.exp(x - c) # 溢出对策
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return y
if __name__ == "__main__":
x = np.arange(-5.0, 5.0, 0.1)
y = softmax(x)
plt.title("softmax")
plt.xlabel("x")
plt.ylabel("y")
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
神经网络和求解机器学习问题的步骤(分成学习和推理两个阶段进行)一样,1、使用神经网络解决问题时,也需要首先使用训练数据(学习数据)进行权重参数的学习;2、进行推理时,使用刚才学习到的参数,对输入数据进行分类。先实现神经网络的“推理处理”。这个推理处理也称为神经网络的前向传播(forward propagation)。
该项目目录如下:
MNIST的图像数据是28像素 × 28像素的灰度图像(1通道),各个像素的取值在0到255之间。每个图像数据都相应地标有“7”“2”“1”等标签。
可以使用dataset/mnist.py文件下载数据集以及模型:
# coding: utf-8
try:
import urllib.request
except ImportError:
raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
'train_img':'train-images-idx3-ubyte.gz',
'train_label':'train-labels-idx1-ubyte.gz',
'test_img':'t10k-images-idx3-ubyte.gz',
'test_label':'t10k-labels-idx1-ubyte.gz'
}
dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"
train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784
def _download(file_name):
file_path = dataset_dir + "/" + file_name
if os.path.exists(file_path):
return
print("Downloading " + file_name + " ... ")
urllib.request.urlretrieve(url_base + file_name, file_path)
print("Done")
def download_mnist():
for v in key_file.values():
_download(v)
def _load_label(file_name):
file_path = dataset_dir + "/" + file_name
print("Converting " + file_name + " to NumPy Array ...")
with gzip.open(file_path, 'rb') as f:
labels = np.frombuffer(f.read(), np.uint8, offset=8)
print("Done")
return labels
def _load_img(file_name):
file_path = dataset_dir + "/" + file_name
print("Converting " + file_name + " to NumPy Array ...")
with gzip.open(file_path, 'rb') as f:
data = np.frombuffer(f.read(), np.uint8, offset=16)
data = data.reshape(-1, img_size)
print("Done")
return data
def _convert_numpy():
dataset = {}
dataset['train_img'] = _load_img(key_file['train_img'])
dataset['train_label'] = _load_label(key_file['train_label'])
dataset['test_img'] = _load_img(key_file['test_img'])
dataset['test_label'] = _load_label(key_file['test_label'])
return dataset
def init_mnist():
download_mnist()
dataset = _convert_numpy()
print("Creating pickle file ...")
with open(save_file, 'wb') as f:
pickle.dump(dataset, f, -1)
print("Done!")
def _change_one_hot_label(X):
T = np.zeros((X.size, 10))
for idx, row in enumerate(T):
row[X[idx]] = 1
return T
def load_mnist(normalize=True, flatten=True, one_hot_label=False):
if not os.path.exists(save_file):
init_mnist()
with open(save_file, 'rb') as f:
dataset = pickle.load(f)
if normalize:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].astype(np.float32)
dataset[key] /= 255.0
if one_hot_label:
dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
if not flatten:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])
if __name__ == '__main__':
init_mnist()
import sys, os
'''sys.path.append(os.pardir) 语句实际上是把父目录 demo加入到 sys.path (Python的搜索模块的路径集)
中,从而可以导入demo下的任何目录(包括 dataset 目录)中的任何文件。
'''
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
from dataset.mnist import load_mnist
# 第一次调用会花费几分钟
'''
1、normalize :设置是否将输入图像正规化为0.0~1.0的值。如果将该参数设置为 False ,
则输入图像的像素会保持原来的0~255
2、第2个参数 flatten 设置是否展开输入图像(变成一维数组)。
'''
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
# 输出各个数据的形状
print(os.pardir) # ..
print(sys.path.append(os.pardir)) # None
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,)
print(x_test.shape) # (10000, 784)
print(t_test.shape) # (10000,)
所需文件:ch03/neuralnet_mnist.py,dataset/mnist.py,common/active.py
import sys, os
sys.path.append(os.pardir)
import pickle
import numpy as np
from dataset.mnist import load_mnist
from common.active import sigmoid, softmax
from PIL import Image
def img_show(img):
pil_img = Image.fromarray(np.uint(img))
pil_img.show()
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)
return x_test, t_test
# init_network() 会读入保存在pickle文件 sample_weight.pkl 中的学习到的权重参数A 。
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
if __name__ == "__main__":
network = init_network()
# print(network['W1'][0].shape) # (784, 50)
# print(network['b1'].shape) # (50, )
# print(network['W2'].shape) # (50, 100)
# print(network['b2'].shape) # (100,)
# print(network['W3'].shape) # (100, 10)
# print(network['b3'].shape) # (10,)
x, t = get_data() # 读取测试集的图片和标签
# img_show(x[0])
print(t[0])
accuracy_cnt = 0
for i in range(len(x)): # 共有10000张测试集图片
y = predict(network, x[i]) # 传入图片进行训练
p = np.argmax(y) # 获取概率最高的元素的指引
if p == t[i]: # 判断训练结果与标签是否一致,检验正确性
accuracy_cnt += 1 # 正确的结果进行累加
print("Accuracy: "+str(float(accuracy_cnt)/len(x))) # 正确的数量除以总的结果数
见上
import numpy as np
import matplotlib.pylab as plt
# 1、阶跃函数的实现
def step_func(x):
return np.array(x > 0, dtype=np.int)
# 2、sigmoid函数的实现
def sigmoid(x):
return 1/(1 + np.exp(-x))
# 3、relu函数的实现
def relu(x):
return np.maximum(0, x)
# 4、不做操作
def identity_function(x):
return x
# 5、输出层的激活函数
def softmax(x):
c = np.max(x)
exp_a = np.exp(x - c) # 溢出对策
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return y
if __name__ == "__main__":
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
print(A1)
print(Z1)
修改ch03/neuralnet_mnist.py
import sys, os
sys.path.append(os.pardir)
import pickle
import numpy as np
from dataset.mnist import load_mnist
from common.active import sigmoid, softmax
from PIL import Image
def img_show(img):
pil_img = Image.fromarray(np.uint(img))
pil_img.show()
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=True, one_hot_label=False)
return x_test, t_test
# init_network() 会读入保存在pickle文件 sample_weight.pkl 中的学习到的权重参数A 。
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
if __name__ == "__main__":
network = init_network()
# print(network['W1'][0].shape) # (784, 50)
# print(network['b1'].shape) # (50, )
# print(network['W2'].shape) # (50, 100)
# print(network['b2'].shape) # (100,)
# print(network['W3'].shape) # (100, 10)
# print(network['b3'].shape) # (10,)
x, t = get_data() # 读取测试集的图片和标签
# img_show(x[0])
print(t[0])
batch_size = 100 # 批数量
accuracy_cnt = 0
for i in range(0, len(x), batch_size): # 共有10000张测试集图片
x_batch = x[i:i+batch_size] # 0-100
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))) # 正确的数量除以总的结果数