Generate...|

园龄:粉丝:关注:

2022-10-27 14:35阅读: 75评论: 0推荐: 0

用numpy实现最简单的前馈神经网络——正向网络建立篇

根据上一篇文章,来构建神经网络吧

  1. 明确输入和输出
  2. 选择合适的各种函数
  3. 矩阵激活函数建立起从输入到输出的拟合函数
  4. 正向传播或反向传播获得损失函数的偏导数(注意对一定的数据集来说自变量为 WWAA 固定)
  5. 梯度下降法努力使损失函数最小

mnist分析(输入分析)

下载

这里下载 mnist数据集

简要说明

images 前16个字节包含了数据的说明,之后的所有字节以 784 字节为一组,是一个个 28×28 像素的图像,像素为一字节的灰度像素,

labels 前8个字节包含了数据的说明,之后的所有字节以 1 字节为一组,是一个个对应着 images 中图像的数字。

加载

以下函数的关键点在于 np.fromfile() 以及 dtype, offset 参数

import numpy as np
from os import listdir
from typing import Dict, NoReturn
S = 784 # area of image
C = 28 # edge length of image
def load_data(dir_path: str) -> Dict[str, np.ndarray]:
"""
加载图像和标签
load images and labels
:param dir_path: directory that contains training files and test files
"""
resource = {}
file_names = listdir(dir_path)
for file_name in file_names:
full_path = path.join(dir_path, file_name)
name, _ = path.splitext(file_name)
if "images" in name:
images = np.fromfile(full_path, dtype=np.uint8, offset=16)
resource[name] = images.reshape(images.size // S, S)
elif "labels" in name:
labels = np.fromfile(full_path, dtype=np.uint8, offset=8)
resource[name] = labels
return resource
# images.shape == (60000, 784)
# labels.shape == (10000, )

显示

from PIL import Image
def display_images(resource, row: int, column: int,
interval: slice = slice(None, None, None)) -> NoReturn:
"""
将多个图像显示在一张图像上
:param row: 行数
:param column: 列数
:param interval: 图像区间
"""
images = Image.new("L", (column * C, row * C))
resource = resource["t10k-images"][interval]
for i in range(column):
for j in range(row):
index = i * row + j
array = resource[index].reshape(C, C)
img = Image.fromarray(array)
images.paste(img, (i * 28, j * 28))
images.show() # 效果如下

mnist

输出分析

显然,输出是 10 个数字中的一个,也就是预测结果。但是一个结果难以使用损失函数确定拟合程度,所以我们希望输出是一个长度为10的向量,包含了每个数字的预测概率

通过普通的矩阵函数产生的输出不一定是概率(就是相加为1且没有负数),可以通过 softmax() 矫正

softmax(ei)=eiie

def softmax(x: np.ndarray) -> np.ndarray:
t = np.exp(x - x.max())
y = np.sum(t, axis=1).reshape(t.shape[0], 1)
return t / y

由输出的概率和标签,通过交叉熵损失函数算出损失

def cross_entropy_error(result: np.ndarray, labels: np.ndarray) -> np.ndarray:
"""
交叉熵损失函数
:param result: 长度为10的向量,包含每个数字的预测概率
:param labels: 图像的标签,即图像的真实数字
"""
return -np.sum(np.log(result[np.arange(labels.size), labels] + 1e-7))

拟合函数建立

激活函数

由于计算过程中可能会出现一定程度的上溢或下溢,并且数字过大也会影响计算

可以使用值域在 (0,1)sigmoid(),同时它的导数也很好计算

sigmoid(x)=11+ez.sigmoid(x)(0,1)sigmoid(x)=ez(1+ez)2=(1sigmoid(x))sigmoid(x)

def sigmoid(x: np.ndarray) -> np.ndarray:
return 1 / (1 + np.exp(-x))
def derivative_sigmoid(x: np.ndarray) -> np.ndarray:
y = sigmoid(x)
return (1 - y) * y

拟合函数

只是演示代码,并不能一定运行

def predict(input_images: np.ndarray, layer_count: int, W: np.ndarray, B: np.ndarray) -> np.ndarray:
"""
获得预测结果的概率
obtain the probabilities of results (namely 0-9)
:param W: 矩阵数组
:param B: 偏置数组
:return: 预测结果概率
"""
layer = input_images
for i in range(layer_count):
layer = sigmoid(layer @ W[i] + B[i])
layer = soft_max(layer)
return layer
# 如果a, b都是np.ndarray
# 那么
# a.dot(b)
# np.dot(a, b)
# a @ b
# 都代表矩阵乘法

input_images 就是待预测图像(28×28 字节),layer_count 是神经网络层数,W 是权重也就是矩阵函数,B 是偏置

这样就完成了一次预测,当然最后的代码会复杂很多

正向计算梯度

只是演示代码,并不能一定运行

def numerical_gradient(input_images: np.ndarray,
w_grad: np.ndarray,
b_grad: np.ndarray) -> NoReturn:
"""
正向求梯度
forward gradient
:param step_length: 训练步长,学习率 learning rate
"""
h = 1e-5 # 求偏导数需要的微小量
for i in range(W.size):
for j in range(W[i].size):
# 这里就是用偏导数定义求偏导数
t = W[i, j]
W[i, j] = t - h
f1 = loss(predict())
W[i, j] = t + h
f2 = loss(predict())
w_grad[i, j] = (f2 - f1) / (2 * h)
W[i, j] = t
for i in range(B.size):
for j in range(B[i].size):
t = B[i, j]
B[i, j] = t - h
f1 = loss(predict())
B[i, j] = t + h
f2 = loss(predict())
b_grad[i, j] = (f2 - f1) / (2 * h)
B[i, j] = t

w_grad 是权重的梯度,b_grad 是偏置的梯度,这样就用梯度下降法了

梯度下降

learning_rate = 0.02
W -= w_grad * learning_rate
B -= b_grad * learning_rate

完成一次梯度下降!

多次重复即可提高拟合度

由于正向求梯度,该方法非常缓慢,可能需要3-4天才能有80%-90%的准确率

下一篇会介绍反向求梯度,可以在十几分钟内达到90%的准确率

最终代码

feedforward-mnist

我也是第一次用github,只放了代码

本文作者:violeshnv

本文链接:https://www.cnblogs.com/violeshnv/p/16832056.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Violeshnv  阅读(75)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 とおいよびごえ 凋叶棕
  2. 2 かぜのねいろ 凋叶棕
  3. 3 Milky Way Train 流派未階堂
  4. 4 nostalgia 流派未階堂
  5. 5 桜花繚乱 はちみつれもん
  6. 6 胡蝶之夢 はちみつれもん
  7. 7 色は散りゆく はちみつれもん
  8. 8 暮色蒼然 はちみつれもん
  9. 9 追想、桜ノ國 はちみつれもん
  10. 10 意にそぐわぬリターニー 凋叶棕
Milky Way Train - 流派未階堂
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.