题外话
这是本人的第一篇作品哦,小白希望大佬们多多指教。
最近捧读了鱼书,想养成做笔记的好习惯~
介绍
本篇文章是对最近学习的成果进行阶段性的总结,介绍了神经网络的最基本的概念以及Python代码的实现。
本文以两层神经网络为例,使用MNIST手写数字图像数字集。
特别声明:本文不包括定义权重和偏置的初值以及超参数的确定等模块,仅仅是对推理过程、梯度下降法的演示
不讲废话进入正题~
各个变量和方法的作用
变量 | 说明 |
---|---|
params | 保存神经网络参数的字典型变量 |
grads | 保存梯度的字典型变量 |
方法 | 说明 |
---|---|
__init__ | 进行权重的初始化 |
predict(self,x) | 进行识别 |
loss(self,x,t) | 计算损失函数的值 |
accuracy(self,x,t) | 计算识别精度 |
numerical_gradient(self,x,t) | 计算权重参数的梯度 |
方法的具体实现
__init__
__init__函数是对权重和偏置的初始化。
def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
#input_size表示输入层的神经元数,hidden_size表示隐藏层的神经元数,output_size表示输出层的神经元数
#初始化权重
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)#第二层偏置(隐藏层到输出层)
通过代码,我们定义了第一层和第二层的权重和偏置。
predict(self,x)
该方法的作用是对各层神经元的计算,根据之前所学,各个神经元的连接计算采用方法是矩阵相乘,在隐藏层需要使用激活函数(这里使用的是sigmoid函数),在输出层我们采用了softmax函数进行分类。
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
def sigmoid(x):
#激活函数
return 1/(1+np.exp(-x))
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
通过代码,我们将数据传入后,进行神经元的推理。
loss(self,x,t)
计算损失函数的值,这里损失函数我们使用的是交叉熵误差
def loss(self,x,t): #计算损失函数的值,x是图像数据,t是正确解标签
y = self.predict(x)
return cross_entropy_error(y,t)
def cross_entropy_error(y,t): #损失函数--交叉熵误差
if y.ndim ==1:
t = t.reshape(1,t.size)
y = y.reshape(1,t.size)
batch_size = y.shape[0]
# h = -np.sum(t*np.log(y+1e-7))/batch_size
# print(h)
return -np.sum(t*np.log(y+1e-7))/batch_size
而我们学习的目的就是为了让损失函数的值变小。所以要沿着梯度的方向不断更新权重。
accuracy(self,x,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])
numerical_gradient(self,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
def _numerical_gradient_no_batch(f, x): #计算梯度
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val # 还原值
return grad
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
两层神经元的实现
上述了各个方法的使用,接下来就是全部代码,在该代码中使用了梯度下降法不断更新权重,学习了10000次,然后经过漫长的等待真的很久!!!(听说用误差发现传播法计算梯度很快),终于实现了!
import pickle
from mnist import load_mnist
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def _numerical_gradient_no_batch(f, x): #计算梯度
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val # 还原值
return grad
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
def sigmoid(x):
#激活函数
return 1/(1+np.exp(-x))
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
def cross_entropy_error(y,t): #损失函数--交叉熵误差
if y.ndim ==1:
t = t.reshape(1,t.size)
y = y.reshape(1,t.size)
batch_size = y.shape[0]
# h = -np.sum(t*np.log(y+1e-7))/batch_size
# print(h)
return -np.sum(t*np.log(y+1e-7))/batch_size
class TwoLayerNet:
def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
#input_size表示输入层的神经元数,hidden_size表示隐藏层的神经元数,output_size表示输出层的神经元数
#初始化权重
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
def loss(self,x,t): #计算损失函数的值,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
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
# #net测试TwoLayerNet
# net = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
# #保存该神经网络的所有参数
# print("定义神经网络的各个参数")
# print(net.params['W1'].shape)# (784, 100)
# print(net.params['b1'].shape)# (100,)
# print(net.params['W2'].shape)# (100, 10)
# print(net.params['b2'].shape)# (10,)
# #神经网络推理过程
# print("输出神经元的推理过程得到结果")
# x = np.random.rand(100,784)
# y=net.predict(x)
# print(y.shape)
# #计算梯度并将参数值保存
# t =np.random.rand(100,10)
# grads = net.numerical_gradient(x,t)
# print("计算梯度并保存")
# print(grads['W1'].shape)#(784, 100)
# print(grads['b1'].shape)#(100,)
# print(grads['W2'].shape)#(100, 10)
# print(grads['b2'].shape)#(10,)
from mnist import load_mnist #minst数据集需要自己下载哦
#通过mini-batch实现
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label=True)
train_loss_list = []
#超参数
iters_num = 10000
train_size = x_train.shape[0] #60000个
batch_size = 100 #100为1捆
learning_rate = 0.1 #学习率
network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
#初始化神经元,输入层784个神经元,隐藏层50个,输出层10个
for i in range(iters_num):
#获取mini-batch
#循环10000次(代表梯度法的更新次数)
batch_mask = np.random.choice(train_size,batch_size)#从全部数据中选取100个,这100个是所有数据的索引
x_batch = x_train[batch_mask] #选取输入数据
t_batch = t_train[batch_mask] #选取标签 one-hot形式
#计算梯度
grad = network.numerical_gradient(x_batch,t_batch)
#更新参数
for key in('W1','b1','W2','b2'):
network.params[key] -= learning_rate*grad[key]
#记录学习过程,loss返回的是交叉熵的值
loss = network.loss(x_batch,t_batch)
print(loss)
train_loss_list.append(loss)