本书重点,主要是实现了一个简单的两层神经网络,并且用它跑了一下mnist数据集
神经网络
激活函数
- 一般是指这样的函数 ,当输入的值超过0时,就输出1,认为是被激活了。
import numpy as np
import matplotlib.pyplot as plt
def step_function(x):
return np.array(x>0,dtype=int)
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()
Sigmoid函数
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.arange(-5.0,5.0,0.1)
y = sigmoid(x);
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()
- 绘制图形比较后可知,Sigmoid函数的曲线更加的平滑。与阶跃函数相比,它输出可以是小数,而不仅仅是0/1。
- 二者都是 非线性函数。在神经网络的 激活函数中,必须使用 非线性函数。线性函数的问题在于,增加层数的时候,总是存在与它等效的 无隐藏层的神经网络。举例: 假如用作为激活函数,选择三层,等效的运算是 ,此时,令是等价的
三层神经网络的实现
- 第一层的加权和可以用以下式子表示,其中是输入信号,W代表了权重,B称为偏置值。
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
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)
W2 = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
B2 = np.array([0.1,0.2])
A2 = np.dot(Z1,W2) + B2
Z2 = sigmoid(A2)
W3 = np.array([[0.1,0.3],[0.2,0.4]])
B3 = np.array([0.1,0.2])
A3 = np.dot(Z2,W3) + B3
Y = A3
print(Y)
输出层的设计
- 一般来说,回归问题用恒等函数,分类函数使用softmax函数
Softmax函数
- softmax函数: 其中,是表示的一个指数函数
- 然而,这个形式的softmax函数很容易溢出,采用分母分子同时乘上一个很小的数的形式来优化此处的 可以是任意常数,一般取最大值的负数。
- 改进后的softmax函数如下:
import numpy as np
import matplotlib.pyplot as plt
def softmax(a):
mx = np.max(a)
exp_a = np.exp(a-mx) #为了防止溢出
sum_exp_a = np.sum(exp_a)
return exp_a / sum_exp_a
性质
- Softmax函数的输出是 0.0~1.0之间的实数,并且softmax函数的输出值 总和为1.,可以把它解释成概率。
神经网络的学习
- 神经网络的特征就是可以 从数据中学习,也就是由数据自动决定权重参数的值。
数据驱动
- 人可以直接使用经验识别出“5”的数字,而机器不行。我们需要从图像中 提取特征量,再用机器学习技术学习特征量的模式。提取特征值后,可以用SVM,KNN等分类器进行学习。
- 在机器学习中,数据分为 训练数据和 测试数据。我们需要在训练数据中寻找最优的参数,然后在 测试数据上正确地评估模型。
损失函数
- 用于评估当前的神经网络对 监督数据在多大程度上不拟合。损失函数越小,越拟合。
- 一般使用 均方误差和交叉熵误差。
均方误差
- 其中,
交叉熵误差
批处理的损失函数
- 对于多组数据来说,我们要把单个的函数值全部都加起来
def cross_entropy_error(y,t):
if y.ndim == 1:
t = t.reshape(1,t.size)
y = y.reshape(1,y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y+1e-7)) / batch_size
mini-batch
- 对于MNIST数据集来说,有60000个图像,全部计算损失函数耗费较大。通常选取 一批小数据来进行训练,
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=True,one_hot_label=True)
train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
print(batch_mask)
x_train = x_train[batch_mask]
print(x_train.shape)
t_train = t_train[batch_mask]
print(t_train.shape)
- 上述代码完成了功能:批量的 随机的取出了单位为10的训练集。
识别精度
- 通常不用它作为评估神经网络的学习好坏,而是用 损失函数,原因是因为该函数大部分情况下 不可微,没有办法利用梯度评估下一步如何该调整参数。
梯度
- 回顾一下梯度的定义——由 偏导数构成的向量,比如说二元函数的梯度
- 在某点的偏导数,用以下式子计算
import numpy as np
def function_2(x):
return x[0]**2 + x[1]**2
def numrcial_gradient(f,x):
h = 1e-4
grad = np.zeros_like(x)
for idx in range(x.size):
tmp = x[idx]
x[idx] = tmp + h
fxh1 = f(x)
x[idx] = tmp - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp #还原值
return grad
grad = numrcial_gradient(function_2,np.array([3.0,4.0]))
print(grad)
# 结果是[6. 8.] 该代码显示了如何接受一个函数和它对应的点,返回对应点的梯度值
- 重要性质:梯度指向函数值减小的最快的方向,梯度的方向 不一定指向最小值,但沿着梯度的方向能最大限度地减小损失函数的值。
梯度法
- 函数的取值从当前函数梯度前进一定距离,重新求梯度,再沿着梯度前行。像这样不断沿着梯度前行,来减少损失函数的值的方法称为 梯度法。
- 用数学公式表达梯度法,如下:
- 该式子中的 表示更新量,称为 学习率。学习率决定再一次学习中,应该学习多少,再多大程度上更新参数。
- 学习率需要 事先设定,过小/过大的效果都不会太好
梯度下降法的实现
- 梯度下降法是指利用梯度法来寻找函数最小值的方法
# 利用梯度下降法,寻找f(x,y)=x^2 + y^2 的极小值
import numpy as np
def function_2(x):
return x[0]**2 + x[1]**2
def numrcial_gradient(f,x):
h = 1e-4
grad = np.zeros_like(x)
for idx in range(x.size):
tmp = x[idx]
x[idx] = tmp + h
fxh1 = f(x)
x[idx] = tmp - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp #还原值
return grad
def gradient_descent(f,init_x,lr=0.01,step_num=100):
# f是要优化的函数,init_x是初始值,lr是learning rate,step_num是要学习的次数
# 梯度下降法就是要找到 极小值的点
x = init_x
for i in range(step_num):
grad = numrcial_gradient(f,x)
print("i is"+str(i)+"x is"+str(x))
x-= lr * grad
return x
init_x = np.array([10.0,10.0])
x = gradient_descent(function_2,init_x=init_x,lr=0.01,step_num=200)
print(x)
# 结果是:[0.17587947 0.17587947],只要把学习次数调高一点,就会越来越靠近[0,0]这个点
- 像 学习率这种无法通过学习调整的参数,需要人为进行设置的参数称为超参数。
神经网路的梯度
- 在神经网络中,损失函数就是梯度中的函数,参数就是指 神经网络中的 权重参数
- 用来表示这个权重参数矩阵,用来表示损失函数
import numpy as np
import matplotlib.pyplot as plt
def softmax(a):
mx = np.max(a)
exp_a = np.exp(a - mx) # 为了防止溢出
sum_exp_a = np.sum(exp_a)
return exp_a / sum_exp_a
# 利用梯度下降法,寻找f(x,y)=x^2 + y^2 的极小值
import numpy as np
def function_2(x):
return x[0] ** 2 + x[1] ** 2
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
idx = it.multi_index
tmp_val = x[idx]
x[idx] = 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 # 値を元に戻す
it.iternext()
return grad
def cross_entropy_error(y,t):
if y.ndim == 1:
t = t.reshape(1,t.size)
y = y.reshape(1,y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y+1e-7)) / batch_size
# 实现一个基本的神经网络
class simpleNet:
def __init__(self):
self.W = np.random.randn(2,3) #用高斯分布进行初始化
def predict(self,x):
return np.dot(x,self.W)
def loss(self,x,t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y,t)
return loss
x = np.array([0.6, 0.9])
t = np.array([0, 0, 1])
net = simpleNet()
f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)
print(dW)
- 该段代码实现了初始化一个大小为 的神经网络,并且设置了其初始权重。然后使用[[#交叉熵误差]] 作为损失函数
两层神经网络的实现
初始化
# coding: utf-8
import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
import numpy as np
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
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
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 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)
dz1 = np.dot(dy, W2.T)
da1 = sigmoid_grad(a1) * dz1
grads['W1'] = np.dot(x.T, da1)
grads['b1'] = np.sum(da1, axis=0)
return grads
- 上述代码完成了一个神经网络的初始化, 权重参数为符合高斯分布的数字进行初始化,偏置值为0.输入神经元大小为784(28 * 28图像大小),输出神经元大小为10(0~9)个数字
梯度下降法寻找最佳参数
import numpy as np
import sys, os
sys.path.append(os.pardir)
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)
train_loss_list = []
iters_num = 10000
train_size = x_train.shape[0]
batch_size =100
learning_rate =0.1
#初始化网络
network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
for i in range(iters_num):
# 随机获取100个数据
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)
print("第"+str(i)+"次训练的loss是")
print(loss)
train_loss_list.append(loss)
plt.plot(train_loss_list)
plt.show()
- 可见,损失函数随着步数的增加逐渐减小
- 在神经网络的学习中,必须确定你的模型是否会 过拟合,也就是模型能正确 识别训练集,但是不能正确地识别 测试集。
epoch
- epoch是指——当整个训练集都被使用过时,此时的 训练次数。比如有1000个数据,每次mini-batch训练要用100个数据,此时的epoch次数就是10.
import numpy as np
import sys, os
sys.path.append(os.pardir)
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)
train_loss_list = []
train_acc_list = []
test_acc_list = []
iters_num = 10000
train_size = x_train.shape[0]
batch_size =100
learning_rate =0.1
iter_per_epoch = max(train_size/batch_size,1)
#初始化网络
network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
for i in range(iters_num):
# 随机获取100个数据
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)
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.show()
- 以上代码每一个epoch就检查每一个节点的准确率,可以发现在训练集和测试集中,二者表现差不多,可以说没有发生 过拟合。