神经网络是由以下基本函数组成:传播函数(包括前向传播,反向传播),激活函数,损失函数
这几个函数的作用: 前向传播:向前预测结果(由已知的参数预测) 激活函数:加入非线性因素,实现映射关系
反向传播:通过前向传播的预测值与实际的值之间的误差利用链式求导法则反向更新参数实现优化 损失函数:损失计算
一个最简单的神经网络就是由上述四个函数构成的,类似于大脑的工作原理。
简单说一下前向传播:前向传播很好理解,就是将上一层的输出作为下一层的输入并计算下一层的输出一直运算到输出层为止,得到输出层的预测结果。
反向传播也很好理解,主要是用到了链式法则,这个知识点在高数下册第九章多元函数微分法及其应用中的第四节中有详细的讲解,至于为什么用链式求导法则来实现反向传播以致于实现参数的更新,我个人理解是导数的一个几何性质可以解释:
这是个导数,f对a求偏导,他的值反应了f在a处变化的快慢,假如这个导数等于0了,从图像上看斜率就是0,他没有变化了,这个时候的参数也就更新得跟实际得一样了。
反向传播理解点这里
整个过程大概是这样:
接下来实现四个函数:
激活函数这里采用了sigmoid,也可以替换成其他比如tanh,sigmod函数的取值范围在(0, 1)之间,可以将网络的输出映射在这一范围,方便分析。层数小可以用,层数大容易出现梯度消失的情况,根据求导可见每一次向前传播都会损失3/4。而且计算量很大。
def sigmoid(x):
s = 1 / (1 + np.exp(-x))
return s
初始化参数w,b
def initialize_with_zeros(dim):
w = np.zeros((dim, 1))
b = 0
return w, b
损失函数
这里用了交叉熵损失
原理看这里
def costCAL(img, Y):
m = img.shape[1]
cost = -np.sum(train_label * np.log(Y) + (1 - train_label) * np.log(1 - Y)) / m
return cost
y是使用激活函数处理后的数值,在0~1上的概率分布。
前向传播:
前向传播的公式如下:
权重w的意义:体现出x的那些像素比较重要,体现在图片上则是x1更多可能是数字的组成部分,x2更多可能是背景。
b的意义:调整神经元向后传递信号的难易程度
在实际写代码时,训练时需要输入6000个图片,一开始我是想用个for循环一个个处理,实际上python提供了一个函数np.dot(矩阵乘积)
A = np.dot(W.T, X)+b
W的数据格式是(784,1),转置后是(1,784)
X的数据格式是(784,6000),6000代表一次计算6000个图片的结果
A的数据格式是(1,6000),A是6000个图片的计算结果合集
def propagate(w, b, img):
# 向前传播
A = np.dot(w.T, img) + b
# 使用激活函数将A的值映射到0~1的区间
Y = sigmoid(A)
return Y
反向传播:
传播函数通过w、b计算出A,通过激活函数又计算出Y
反向传播函数通过Y计算出dw和db
使用dw和db,通过梯度下降的方法得到新的w和b
使用更新后的w和b重复前面的运算过程
def back_propagate(Y, img, label):
m = img.shape[1]
dZ = Y - label
dw = np.dot(img, dZ.T) / m
db = np.sum(dZ) / m
return dw, db
更新参数
def optimize(img, label, w, b, num_iterations, learning_rate, print_cost):
# 梯度下降法,循环num_iterations次找到最优w和b
for i in range(num_iterations):
# 向前传播一次
Y = propagate(w, b, img)
# 计算成本
cost = costCAL(img, label, Y)
# 反向传播得到dw、db
dw, db = back_propagate(Y, img, label)
# 更新w和b
w = w - learning_rate * dw
b = b - learning_rate * db
# 每100次输出一下成本
if i % 100 == 0:
if print_cost:
print('优化%i次后成本是:%f' % (i, cost))
return w, b
预测函数
def predict(w, b, img):
m = img.shape[1]
Y_prediction = np.zeros((1, m))
# 向前传播得到Y
Y = propagate(w, b, img)
for i in range(Y.shape[1]):
# 若Y的值大于等于0.5就认为该图片为数字9,否则不是数字9
if Y[0, i] >= 0.5:
Y_prediction[0, i] = 1
return Y_prediction
最终结果:
踩坑日记:
我想换激活函数relu但是忽略了后面损失函数log是不能取0的,那么就会出现下面这种情况。
完整代码
import math
import sys, os
import numpy as np
from PIL import Image
from mnist import init_mnist
from mnist import load_mnist
# 设置np输出数组时不做缩写
np.set_printoptions(edgeitems=1000000)
# 下载数据集,会下载6W个训练数据核1W个测试数据
init_mnist()
# 加载数据集 数据集包括一张图片和一个正确标注
(train_img, train_label), (test_img, test_label) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
# 将label不是9的数据全部转为0,将9转为1
train_label = np.where(train_label == 9, 1, 0)
test_label = np.where(test_label == 9, 1, 0)
# 修改数组的大小
train_img = np.resize(train_img, (6000, train_img.shape[1]))
train_label = np.resize(train_label, (6000, 1))
test_img = np.resize(test_img, (1000, test_img.shape[1]))
test_label = np.resize(test_label, (1000, 1))
# 需要将数据进行转置
train_img = train_img.T
train_label = train_label.T
test_img = test_img.T
test_label = test_label.T
# sigmoid函数
def sigmoid(x):
s = 1 / (1 + np.exp(-x))
return s
# 初始化权重数组w和偏置b(默认为0)
def initialize_with_zeros(dim):
w = np.zeros((dim, 1)) # 全零的数组
b = 0
return w, b
# 通过Y和标注计算成本
def costCAL(img, label, Y):
m = img.shape[1]
cost = -np.sum(train_label * np.log(Y) + (1 - train_label) * np.log(1 - Y)) / m
return cost
# 向前传播得到Y
def propagate(w, b, img):
m = img.shape[1]
# 向前传播
A = np.dot(w.T, img) + b
# 使用激活函数将A的值映射到0~1的区间
Y = sigmoid(A)
return Y
# 反向传播得到dw、db
def back_propagate(Y, img, label):
m = img.shape[1]
dZ = Y - label
dw = np.dot(img, dZ.T) / m
db = np.sum(dZ) / m
return dw, db
# 通过梯度下降法更新w和b
def optimize(img, label, w, b, num_iterations, learning_rate, print_cost):
# 梯度下降法,循环num_iterations次找到最优w和b
for i in range(num_iterations):
# 向前传播一次
Y = propagate(w, b, img)
# 计算成本
cost = costCAL(img, label, Y)
# 反向传播得到dw、db
dw, db = back_propagate(Y, img, label)
# 更新w和b
w = w - learning_rate * dw
b = b - learning_rate * db
# 每100次输出一下成本
if i % 100 == 0:
if print_cost:
print('优化%i次后成本是:%f' % (i, cost))
return w, b
# 预测函数
def predict(w, b, img):
m = img.shape[1]
Y_prediction = np.zeros((1, m))
# 向前传播得到Y
Y = propagate(w, b, img)
for i in range(Y.shape[1]):
# 若Y的值大于等于0.5就认为该图片为数字9,否则不是数字9
if Y[0, i] >= 0.5:
Y_prediction[0, i] = 1
return Y_prediction
# 按训练图片的数量生成w和b,
w, b = initialize_with_zeros(train_img.shape[0])
# 通过梯度下降法更新w和b
w, b = optimize(train_img, train_label, w, b, 2000, 0.005, True)
Y_prediction_train = predict(w, b, train_img)
Y_prediction_test = predict(w, b, test_img)
print('对训练图片的预测准确率为:{}%'.format(100 - np.mean(np.abs(Y_prediction_train - train_label)) * 100))
print('对测试图片的预测准确率为:{}%'.format(100 - np.mean(np.abs(Y_prediction_test - test_label)) * 100))



