1.从感知机到神经网络
感知机理论上可以将计算机进行的复杂处理表示出来,但设定权重的工作现在还是由人工进行的,神经网络的出现就是为了解决这一缺陷,具体来讲,神经网络的一个重要性质就是它可以自动地从数据中学习到合适的权重参数。
将输入信号的和用函数h(x)转换,转换后的值为输出y,h(x)称为激活函数,即激活函数的作用在于决定如何来激活输入信号的总和。
补充:(1)“朴素感知机”是指单层网络,激活函数使用阶跃函数;(2)“多层感知机”指神经网络,使用sigmoid等平滑的激活函数
2.激活函数
阶跃函数:
import numpy as np
x=np.array([-1.0,1.0,2.0])
y=x>0 # bool型
y=y.astype(int) # astype()通过参数指定期望的类型,此处为int型
print(y) # [0,1,1]
import matplotlib.pylab as plt # pylab结合了pyplot和numpy,对交互式使用更方便
def step_function(x):
return np.array(x>0,dtype=int) # 支持np数组
# type()返回数据结构类型,如list、dict等,dtype()返回数据元素的数据类型,如int、float等
x=np.arange(-5,5,0.1)
y=step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1) # 指定y轴的范围
plt.show()
sigmoid函数:
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
x=np.arange(-5,5,0.1)
y=sigmoid(x)
# sigmoid函数支持np数组因为np的广播功能,即标量与np数组运算会和数组中每个元素进行运算
"""
相对于阶跃函数只能返回0或1,sigmoid函数可以返回0.880等实数
即:感知机中神经元之间流动的是0或1的二元信号,而神经网络中流动的是连续的实数值信号
"""
阶跃函数与sigmoid函数的共同点是两者均为非线性函数,神经网络的激活函数必须为非线性函数,使用线性函数则加层无意义。
Relu函数:
import numpy as np
import matplotlib.pyplot as plt
def relu(x):
return np.maximum(0,x) # maximum()从输入的数值中选择较大的值进行输出
3.多维数组的运算
np.ndim() 查看数组维数;A.shape返回数组形状,为元组类型,一维数组的情况下也要返回和多维数组的情况下一致的结果,例:(4,);np.dot()返回数组的点积
A=np.array([[1,2],[3,4],[5,6]])
print(A.shape) # (3,2)
B=np.array([7,8])
print(B.shape) # (2,)
C=np.dot(A,B) # (3,)/A是二维矩阵,B是一维数组,保持对应维度的元素个数一致
4.三层神经网络的实现
# 进行权重和偏置的初始化,并将它们保存在字典变量network中
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): # forward()表示从输入到输出
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)
输出层的激活函数用σ()表示,不同于隐藏层的激活函数h()
5.输出层的设计
输出层所用的激活函数,要根据求解问题的性质决定。回归问题(根据某个输入预测一个连续的数值的问题)可以使用恒等函数,分类问题(数据属于哪一个类别的问题)可以使用softmax函数。
softmax函数:
def softmax_1(a):
c=np.max(a)
exp_a=np.exp(a-c) # 溢出对策(指数函数的值很容易变得非常大,可能返回表示无穷大的inf)
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_1(a)
# 计算机在处理”数“时,数值必须在4字节或8字节的有限数据宽度内
# 否则会出现超大值无法表示的问题,即溢出问题,直接定义softmax函数即可能产生溢出问题
性质:softmax函数的输出总和总为1,因此把softmax函数的输出解释为“概率”
一般神经网络只把输出值最大的神经元所对应的类别作为识别结果,即使使用softmax函数,输出值最大的神经元的位置也不会变(指数函数是单调函数),因此神经网络(学习+推理)在进行推理阶段时,输出层的softmax函数可以省略
6.手写数字识别
读入数据:
import sys,os
sys.path.append(os.pardir)
# 把父目录加入到sys.path中,从而可以导入父目录下的任何目录中的任何文件
from dataset.mnist import load_mnist
# load_mnist函数以“(训练图像,训练标签),(测试图像,测试标签)”的形式返回读入的MNIST数据
# normalize设置是否将输入图像正规化为0。0~1.0的值,False为保持原来的0~255
# flatten设置是否展开图像为一维数组,False为1×28×28的三维数组,True为784个元素构成的一维数组
# one_hot_label设置是否将标签保存为one-hot表示,。one-hot表示仅正确解标签为1,其余皆为0的数组,False表示像7、2这样简单保存正确解标签
(x_train,t_train),(x_test,t_test)=load_mnist(flatten=True,normalize=False,one_hot_label=False)
# 输出各组数据的形状
print(x_train.shape) # (60000,784)
print(t_train.shape) # (60000,)
print(x_test.shape) # (10000,784)
print(t_test.shape) # (10000,)
# pickle功能可以将程序运行中的对象保存为文件,加载保存过的pickle文件,可以立刻复原之前程序运行中的对象
# 用于读入MNIST数据集的load_mnist()函数内部也使用了pickle功能(在第2次及以后读入时)
显示图像:
from PIL import Image # 图像显示使用PIL(Python Image Library)模块
def img_show(img):
pil_img=Image.fromarray(np.uint8(img))
# Image.fromarray()将保存为NumPy数组的图像数据转换为PIL用的数据对象
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)
print(img.shape) # (784,),因为flatten=True
img=img.reshape(28,28) # flatten=True时读入的是一维数组,显示图像时需要将其变回28×28
img_show(img)
神经网络的推理处理:(输入层784个神经元,输出层10个神经元)
import pickle
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
# 以字典变量的形式保存了权重和偏置参数(保存在pickle文件中的)
def init_network():
with open("sample_weight.pkl","rb") as f:
network = pickle.load(f)
# 假设学习已经完成且学习到的参数保存在sample_weight.pkl中,推理阶段直接加载
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)
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)): # for语句逐一取出保存在x中的图像数据
y=predict(network,x[i]) # predict函数以np数组形式输出各个标签对应的概率,即10000个(10,)数组
p=np.argmax(y) # 获取概率最高的元素的索引
if p==t[i]:
accuracy_cnt+=1
print("Accurary:"+str(float(accuracy_cnt)/len(x)))
normalize设置为True,函数内部会进行转换,将图像各个像素除以255,使数据的值在0.0~1.0之内,称为正规化,是预处理的一种;此外,利用数据整体的均值或标准差,移动数据,使数据整体以0为中心分布;还有将数据整体的分布形状均匀化的方法,即数据白化等,都是预处理的方法。
批处理:
x,t=get_data()
network=init_network()
batch_size=100 # 批数量(一次性输出100个结果)
accuracy_cnt=0
#range(start,end,step)生成列表中的下一个元素会增加step指定的值,本例中即[0,100,200...,9900]
for i in range(0,len(x),batch_size):
x_batch=x[i:i+batch_size] # 取出从第i个到第i+batch_size个之间的数据,如x[0:100](第0行到第99行)、x[100:200]等
y_batch=predict(network,x_batch) # (100,10)
p=np.argmax(y_batch,axis=1) # axis=1指定了在100×10的数组中,沿着第一维方向寻找值最大元素的索引(矩阵的第0维是列方向,第1维是行)
accuracy_cnt+=np.sum(p==t[i:i+batch_size]) # 使用比较运算符==生成bool型数组,并利用np.sum()计算True的个数
print("Accuracy:"+str(float(accuracy_cnt)/len(x)))
补充:matrix矩阵 row行 column列 forward propagation前向传播 normalization正规化 whitening白化 pre-processing预处理 batch批