当前位置: 首页 >  软件学堂 >  《深度学习入门·基于Python的理论与实现》代码笔记

《深度学习入门·基于Python的理论与实现》代码笔记

导读:花了4天看完了《深度学习入门·基于Python的理论与实现》这本书 然后用一天时间自己试着实现了一下这里面的代码.其中后面深层卷积神经网络的部分有点难 我尝试着实现了一下结果发现有好多bug 由于马上要开始复习期末考试了就不继续深究了 今天就是寒假弄这个的最后一天.写个随笔记录一

花了4天看完了《深度学习入门·基于Python的理论与实现》这本书 然后用一天时间自己试着实现了一下这里面的代码

其中后面深层卷积神经网络的部分有点难 我尝试着实现了一下结果发现有好多bug 由于马上要开始复习期末考试了就不继续深究了 今天就是寒假弄这个的最后一天 写个随笔记录一下 之后就好好复习了

代码部分关键的地方我都放了注释所以文章就不提具体实现了 前面看一下几个运行的结果和反映出来的一些现象 后面放源代码 之后相关的一些文件包括全书pdf也会放在博客里面

水平很差 如果有问题欢迎批评指正!

目录

  • 实验数据与现象
    • TwoLayerNet_Numerical
    • TwoLayerNet_BackPropagation
    • MultiLayerNet
  • 源代码
    • mnist.py
    • main.py

实验数据与现象

TwoLayerNet_Numerical

  • 使用的是数值微分梯度下降

  • 学习10次 时间五分半 太耗时了 所以这个数值微分只能用来进行梯度验证的时候用一下

TwoLayerNet_BackPropagation

  • 用反向传播法求了梯度 学习100000次 仅仅耗时了一分半 显著提高效率 结果上 训练集准确率达到99.3% 测试集准确率97.1%

MultiLayerNet

  • 多层神经网络 因为模块化 所以可以任意更改中间隐藏层的大小 上面的两个TwoLayerNet类只是用来练习用的 之后详细扩展就需要这种可拓展性比较好的实现

其中Width是隐藏层神经元的个数 Layers是隐藏层的层数

  • 不同的层数 最终学习情况如下:

    • 2层网络

学习50000次 耗时:157.87秒

训练集准确率为97.11% 验证集准确率为96.63%

* 3层网络

学习50000次 耗时:182.40秒

训练集准确率为98.58% 验证集准确率为97.17%

* 4层网络

学习50000次 耗时:194.60秒

训练集准确率为99.34% 验证集准确率为97.36%

* 8层网络

学习50000次 耗时:285.70秒

训练集准确率为99.96% 验证集准确率为96.78%

* 16层网络

学习50000次 耗时:448.04秒

训练集准确率为99.67% 验证集准确率为96.76%

* 32层网络

学习50000次 耗时:703.78秒

训练集准确率为99.46% 验证集准确率为96.68%

* 50层网络

学习50000次 耗时:969.98秒

训练集准确率为9.87% 验证集准确率为9.80% (??? 炸了?????)

* 64层网络

学习50000次 耗时:1269.11秒

训练集准确率为52.07% 验证集准确率为53.25%

  • 很显然 层数太大了之后 爆炸了

有可能是隐藏层的总参数太多 导致有一些神经元在学习的过程中死亡了 最终导致结果的崩盘

抛开爆炸的那些不谈 能够发现随着层数的增加 训练集的准确率慢慢的和验证集准确率越分越远 也就是说慢慢的有过拟合的问题出现了 如果训练集的数据不是60000组而是600组 那这个问题就会非常明显了

我们将训练集调整为600组 学习次数50000次 层数为5层 mini-batch大小设置为10 尝试一下:

3层网络 学习50000次 耗时:111.24秒 训练集准确率为100.00% 验证集准确率仅为86.13%

这个很显然是发生了过拟合情况

  • 为了解决过拟合 可以在我们MultiLayerNet类的实现里面的loss函数 增添正则化参数 (λ*W^2)/2

添加之后我们再查看一下学习情况:

3层网络 学习50000次 耗时:209.86秒 训练集准确率为100.00% 验证集准确率为92.76%

能够发现过拟合的问题虽然还是有 但是好了很多 测试集的准确率也上升了不少 说明正则化还是有效的

源代码

下面就是源代码部分 其中mnist.py是直接偷过来的 用处是获取mnist数据集 main.py就是自己看书实现的一些神经网络

mnist.py

# coding: utf-8

# 这个读取mnist的程序是纯拿过来用的  所以具体实现也不是很会
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np

url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
    'train_img': 'train-images-idx3-ubyte.gz',
    'train_label': 'train-labels-idx1-ubyte.gz',
    'test_img': 't10k-images-idx3-ubyte.gz',
    'test_label': 't10k-labels-idx1-ubyte.gz'
}

dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"

train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784


def _download(file_name):
    file_path = dataset_dir + "/" + file_name

    if os.path.exists(file_path):
        return

    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")


def download_mnist():
    for v in key_file.values():
        _download(v)


def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name

    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
        labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")

    return labels


def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name

    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
        data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")

    return data


def _convert_numpy():
    dataset = {}
    dataset['train_img'] = _load_img(key_file['train_img'])
    dataset['train_label'] = _load_label(key_file['train_label'])
    dataset['test_img'] = _load_img(key_file['test_img'])
    dataset['test_label'] = _load_label(key_file['test_label'])

    return dataset


def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")


def _change_one_hot_label(X):
    T = np.zeros((X.size, 10))
    for idx, row in enumerate(T):
        row[X[idx]] = 1

    return T


def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """读入MNIST数据集

    Parameters
    ----------
    normalize : 将图像的像素值正规化为0.0~1.0
    one_hot_label :
        one_hot_label为True的情况下,标签作为one-hot数组返回
        one-hot数组是指[0,0,1,0,0,0,0,0,0,0]这样的数组
    flatten : 是否将图像展开为一维数组

    Returns
    -------
    (训练图像, 训练标签), (测试图像, 测试标签)
    """
    if not os.path.exists(save_file):
        init_mnist()

    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)

    if normalize:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0

    if one_hot_label:
        dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
        dataset['test_label'] = _change_one_hot_label(dataset['test_label'])

    if not flatten:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].reshape(-1, 1, 28, 28)

    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])


if __name__ == '__main__':
    init_mnist()

main.py

import os
import sys
import numpy as np

sys.path.append(os.pardir)
from mnist import load_mnist  # 获取mnist数字图像数据集
from PIL import Image  # 显示mnist数据用
from matplotlib import pyplot as plt  # 画函数图像
import time
from collections import OrderedDict


# 显示mnist数据图像
def img_show(img):
    img = img.reshape(28, 28)
    if type(img[0][0]).__name__ == 'float32':  # 如果进行了正规化 则乘255变为int形式
        img *= 255.0
    pil_img = Image.fromarray(np.uint8(img))  # 将img矩阵的每一个像素转换为8位int  并通过转换函数转换为Image类型
    pil_img.show()


# 激活函数
## sigmoid
def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))  # 没有分母为0的可能 所以不用加保护量


## ReLU
def relu(x):
    return np.maximum(x, 0)  # x>0就返回x x<0就返回0


# 代价函数
## 均方误差
def mean_square_error(y, t):
    return np.sum((y - t) ** 2) / 2.0  # ans = sum((y-t)^2)/2


## 交叉熵误差(mini-batch版本)
def cross_entropy_error(y, t):
    if y.ndim == 1:  # 输出数组的维数是1  那么需要转换一下形状以确保统一
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
    if t.size == y.size:
        t = t.argmax(axis=1)  # 提取出每一行最大值  即把向量形式的监督值转换为数字形式

    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size  # 注意这里面加了一个1e-7以防出现log(0)


#  还有一个注意事项: 原版的交叉熵是要求和 而我们的mnist数据集的t监督向量只有一个是1  所以也不需要求和了 找到对应的那个y值放进log就行


# 数值微分求梯度
def numerical_gradient(f, x):
    dx = 1e-4;  # 数值微分的小微元dx
    grads = np.zeros_like(x)  # 生成和x一样大小的梯度向量 等待填入计算结果
    if x.ndim == 2:
        xshape_x = x.shape[1]
        for idx in range(x.size):
            idx_x = idx // xshape_x
            idx_y = idx % xshape_x
            temp = x[idx_x][idx_y]  # 临时存储idx处的x值
            x[idx_x][idx_y] = temp + dx
            f1 = f(x)
            x[idx_x][idx_y] = temp - dx
            f2 = f(x)
            grads[idx_x][idx_y] = (f1 - f2) / (2 * dx)
            x[idx_x][idx_y] = temp  # 还原x
        return grads
    if x.ndim == 1:
        for idx in range(x.size):
            temp = x[idx]  # 临时存储idx处的x值
            x[idx] = temp + dx
            f1 = f(x)
            x[idx] = temp - dx
            f2 = f(x)
            grads[idx] = (f1 - f2) / (2 * dx)
            x[idx] = temp  # 还原x
        return grads


# 神经网络各层的实现
## Sigmoid层
class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):  # 前向
        out = self.out = sigmoid(x)  # 最好返回一个新的变量  不然可能会导致类的封装性受损
        return out

    def backward(self, dout):  # 反向
        dx = dout * (1.0 - self.out) * self.out  # 反向传播在传过来的dout基础上乘上y(1-y)
        return dx


## Relu层
class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()  # 注意python变量赋值自动算作是引用 这里不能更改x所以要copy它
        out[self.mask] = 0
        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        return dx


## Affine层
class Affine:
    def __init__(self, w, b):  # 构造函数传入该层的权重w和偏置b
        self.out = None
        self.w = w
        self.b = b
        self.original_x_shape = None
        self.x = None
        self.dx = None  #
        self.dw = None  # 这两个是反向传播过来的梯度大小

    def forward(self, x):
        # 对应张量
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x
        out = np.dot(self.x, self.w) + self.b
        return out

    def backward(self, dout):
        dx = np.dot(dout, self.w.T)  # dx = dout * w'
        self.dw = np.dot(self.x.T, dout)  # dw = x' * dout
        self.db = np.sum(dout, axis=0)  # db = dout 在 竖直方向的求和
        dx = dx.reshape(*self.original_x_shape)  # 还原输入数据的形状(对应张量)
        return dx


## Softmax输出层
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T
    x = x - np.max(x)  # 溢出对策 调整参数防止e^x过大
    return np.exp(x) / np.sum(np.exp(x))


class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None  # 输出数据
        self.t = None  # 监督数据 即正确答案 毕竟输出层需要给出代价函数

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)  # 使用softmax进行标准化
        loss = self.loss = cross_entropy_error(self.y, self.t)  # 使用交叉熵误差来计算代价函数
        return loss

    def backward(self, dout=1):  # 因为是最后一层  所以dout通常是1
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size  # 这里有个minibatch的调整
        return dx


## Dropout层
class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio  # 将几成比例的神经元给忽略静默掉
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            # 注意这里是rand而不是randn  因为需要的是均匀分布的随机数 而非高斯分布
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)  # 如果flg是False  注意这里是如何前向传播的

    def backward(self, dout):
        return dout * self.mask  # mask不变 前后项一样


# 卷积神经网络各层的实现
## im2col和col2im函数实现(书中提供的实现)
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """

    Parameters
    ----------
    input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据
    filter_h : 滤波器的高
    filter_w : 滤波器的长
    stride : 步幅
    pad : 填充

    Returns
    -------
    col : 2维数组
    """
    N, C, H, W = input_data.shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1

    img = np.pad(input_data, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant')
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))

    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N * out_h * out_w, -1)
    return col


def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    """

    Parameters
    ----------
    col :
    input_shape : 输入数据的形状(例:(10, 1, 28, 28))
    filter_h :
    filter_w
    stride
    pad

    Returns
    -------

    """
    N, C, H, W = input_shape
    out_h = (H + 2 * pad - filter_h) // stride + 1
    out_w = (W + 2 * pad - filter_w) // stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride * out_h
        for x in range(filter_w):
            x_max = x + stride * out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]


## 卷积层
class Convolution:
    def __init__(self, w, b, stride=1, pad=0):
        self.w = w
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中间数据(backward时使用)
        self.x = None
        self.col = None
        self.col_w = None

        self.dw = None
        self.db = None

    def forward(self, x):
        FN, C, FH, FW = self.w.shape
        N, C, H, W = x.shape
        out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
        out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)  # 计算出卷积之后的结果形状

        col = im2col(x, FH, FW, self.stride, self.pad)  # Image to Column  将输入数据展开
        col_w = self.w.reshape(FN, -1).T

        out = np.dot(col, col_w)
        out += self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_w = col_w

        return out

    def backward(self, dout):
        FN, C, FH, FW = self.w.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dw = np.dot(self.col.T, dout)
        self.dw = self.dw.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_w.T)
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx


## 池化层
class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h * self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,))

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx


# 更新梯度方法
## SGD随机梯度下降方法(最常用)
class SGD:
    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]


## Momentum动量方法
class Momentum:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}  # 速度也是每一个参数对应一个速度
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)
        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]  # αv-lr*grads
            params[key] += self.v[key]


## AdaGrad方法
class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)  # 就是照着书上那个公式来实现


# 一些神经网络类
## 2层神经网络(数值微分)
class TwoLayerNet_Numerical:
    def __init__(self, input_size, hidden_size, output_size,
                 weight_init_std=0.01):  # weight_init_std是权重初始值随机高斯分布的标准差 因为需要让权重尽量小
        self.params = {}  # 权重参数字典
        self.params['w1'] = weight_init_std * np.random.randn(input_size, hidden_size)  # 权重 初始化为较小的高斯分布
        self.params['b1'] = np.zeros(hidden_size)  # 偏置 初始化为0
        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 = sigmoid(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(y.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['w2'] = numerical_gradient(loss_w, self.params['w2'])
        grads['b1'] = numerical_gradient(loss_w, self.params['b1'])
        grads['b2'] = numerical_gradient(loss_w, self.params['b2'])
        return grads


## 2层神经网络(反向传播)
class TwoLayerNet_BackPropagation:
    def __init__(self, input_size, hidden_size, output_size,
                 weight_init_std=0.01):  # weight_init_std是权重初始值随机高斯分布的标准差 因为需要让权重尽量小
        self.params = {}  # 权重参数字典
        self.params['w1'] = np.random.randn(input_size, hidden_size) / np.sqrt(input_size)  # 权重 初始化方法是Xavier高斯分布
        self.params['b1'] = np.zeros(hidden_size)  # 偏置 初始化为0
        self.params['w2'] = np.random.randn(hidden_size, output_size) / np.sqrt(hidden_size)
        self.params['b2'] = np.zeros(output_size)
        # 生成层
        self.layers = OrderedDict()  # 注意是有序字典  而且这里存放的是除了最后一层以外的所有层  最后一层有特殊用处
        self.layers['Affine1'] = Affine(self.params['w1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['w2'], self.params['b2'])
        self.lastlayer = SoftmaxWithLoss();

    def predict(self, x):
        for layer in self.layers.values():  # 注意这里的语法.values()
            x = layer.forward(x)
        return x

    def loss(self, x, t):  # 求算代价
        y = self.predict(x)
        return self.lastlayer.forward(y, t)

    def accuracy(self, x, t):  # 求算网络准确值
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1: t = np.argmax(t, axis=1)
        accuracy = np.sum(y == t) / float(y.shape[0])
        return accuracy

    def gradient(self, x, t):
        self.loss(x, t)
        # 反向传播
        dout = 1
        dout = self.lastlayer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()  # 顺序倒置  目的就是反向传播
        for layer in layers:
            dout = layer.backward(dout)
        grads = {}
        grads['w1'] = self.layers['Affine1'].dw
        grads['w2'] = self.layers['Affine2'].dw
        grads['b1'] = self.layers['Affine1'].db
        grads['b2'] = self.layers['Affine2'].db
        return grads

    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['w2'] = numerical_gradient(loss_w, self.params['w2'])
        grads['b1'] = numerical_gradient(loss_w, self.params['b1'])
        grads['b2'] = numerical_gradient(loss_w, self.params['b2'])
        return grads

    def gradient_check(self, x, t):
        x_check = x[:5]
        t_check = t[:5]
        grad_numerical = self.numerical_gradient(x_check, t_check)
        grad_backprop = self.gradient(x_check, t_check)
        # 求平均值
        for key in grad_backprop.keys():
            diff = np.average(np.abs(grad_numerical[key] - grad_backprop[key]))
            print(key + ":" + str(diff))
        return


## 多层神经网络(书中提供的实现)
class MultiLayerNet:
    def __init__(self, input_size, hidden_size_list, output_size,
                 activation='relu', weight_init_std='relu', weight_decay_lambda=0):
        self.input_size = input_size
        self.output_size = output_size
        self.hidden_size_list = hidden_size_list
        self.hidden_layer_num = len(hidden_size_list)
        self.weight_decay_lambda = weight_decay_lambda
        self.params = {}

        # 初始化权重
        self.__init_weight(weight_init_std)

        # 生成层
        activation_layer = {'sigmoid': Sigmoid, 'relu': Relu}
        self.layers = OrderedDict()
        for idx in range(1, self.hidden_layer_num + 1):
            self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
                                                      self.params['b' + str(idx)])
            self.layers['Activation_function' + str(idx)] = activation_layer[activation]()

        idx = self.hidden_layer_num + 1
        self.layers['Affine' + str(idx)] = Affine(self.params['W' + str(idx)],
                                                  self.params['b' + str(idx)])

        self.last_layer = SoftmaxWithLoss()

    def __init_weight(self, weight_init_std):  # 设定权重的初始值   可以用Sigmoid或者ReLU
        all_size_list = [self.input_size] + self.hidden_size_list + [self.output_size]
        for idx in range(1, len(all_size_list)):
            scale = weight_init_std
            if str(weight_init_std).lower() in ('relu', 'he'):
                scale = np.sqrt(2.0 / all_size_list[idx - 1])  # 使用ReLU的情况下推荐的初始值
            elif str(weight_init_std).lower() in ('sigmoid', 'xavier'):
                scale = np.sqrt(1.0 / all_size_list[idx - 1])  # 使用sigmoid的情况下推荐的初始值

            self.params['W' + str(idx)] = scale * np.random.randn(all_size_list[idx - 1], all_size_list[idx])
            self.params['b' + str(idx)] = np.zeros(all_size_list[idx])

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        return x

    def loss(self, x, t):
        y = self.predict(x)
        weight_decay = 0
        for idx in range(1, self.hidden_layer_num + 2):
            W = self.params['W' + str(idx)]
            weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)
        return self.last_layer.forward(y, t)
        # return self.last_layer.forward(y, t) + weight_decay  # 后面加上weight_decay是权值参数正则化

    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 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 = {}
        for idx in range(1, self.hidden_layer_num + 2):
            grads['W' + str(idx)] = numerical_gradient(loss_W, self.params['W' + str(idx)])
            grads['b' + str(idx)] = numerical_gradient(loss_W, self.params['b' + str(idx)])

        return grads

    def gradient(self, x, t):
        # forward
        self.loss(x, t)
        # backward
        dout = 1
        dout = self.last_layer.backward(dout)
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        grads = {}
        for idx in range(1, self.hidden_layer_num + 2):
            grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dw + self.weight_decay_lambda * self.layers[
                'Affine' + str(idx)].w
            grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db
        return grads


# 进行神经网络训练的类(书中提供的代码 目前暂时没用 以后可以拿过来扩展一下)
class Trainer:
    def __init__(self, network, x_train, t_train, x_test, t_test,
                 epochs=20, mini_batch_size=100,
                 optimizer='SGD', optimizer_param={'lr': 0.01},
                 evaluate_sample_num_per_epoch=None, verbose=True):
        self.network = network
        self.verbose = verbose
        self.x_train = x_train
        self.t_train = t_train
        self.x_test = x_test
        self.t_test = t_test
        self.epochs = epochs
        self.batch_size = mini_batch_size
        self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch

        # optimzer
        optimizer_class_dict = {'sgd': SGD, 'momentum': Momentum, 'adagrad': AdaGrad}
        self.optimizer = optimizer_class_dict[optimizer.lower()](**optimizer_param)

        self.train_size = x_train.shape[0]
        self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
        self.max_iter = int(epochs * self.iter_per_epoch)
        self.current_iter = 0
        self.current_epoch = 0

        self.train_loss_list = []
        self.train_acc_list = []
        self.test_acc_list = []

    def train_step(self):
        batch_mask = np.random.choice(self.train_size, self.batch_size)
        x_batch = self.x_train[batch_mask]
        t_batch = self.t_train[batch_mask]

        grads = self.network.gradient(x_batch, t_batch)
        self.optimizer.update(self.network.params, grads)

        loss = self.network.loss(x_batch, t_batch)
        self.train_loss_list.append(loss)
        if self.verbose: print("train loss:" + str(loss))

        if self.current_iter % self.iter_per_epoch == 0:
            self.current_epoch += 1

            x_train_sample, t_train_sample = self.x_train, self.t_train
            x_test_sample, t_test_sample = self.x_test, self.t_test
            if not self.evaluate_sample_num_per_epoch is None:
                t = self.evaluate_sample_num_per_epoch
                x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]
                x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]

            train_acc = self.network.accuracy(x_train_sample, t_train_sample)
            test_acc = self.network.accuracy(x_test_sample, t_test_sample)
            self.train_acc_list.append(train_acc)
            self.test_acc_list.append(test_acc)

            if self.verbose: print(
                "=== epoch:" + str(self.current_epoch) + ", train acc:" + str(train_acc) + ", test acc:" + str(
                    test_acc) + " ===")
        self.current_iter += 1

    def train(self):
        for i in range(self.max_iter):
            self.train_step()

        test_acc = self.network.accuracy(self.x_test, self.t_test)

        if self.verbose:
            print("=============== Final Test Accuracy ===============")
            print("test acc:" + str(test_acc))


# 主函数main()部分 用类实现 以便多个神经网络不冲突在一块儿
class Main:
    def tryTwoLayerNet_Numerical(self):
        """
            一个简单的二层神经网络
            其中激活函数采用sigmoid  初始化权重采用普通SGD  代价函数采用交叉熵误差
            未采用模块化神经层
            **求梯度采用数值微分**  下降时采用固定学习率
            使用mini-batch优化
            运行情况:

        """
        start_time = time.time()
        (x_train, t_train), (x_test, t_test) = load_mnist(one_hot_label=True, normalize=True)  # 获取mnist
        train_loss_list = []  # 训练代价函数变化情况记录在列表里
        # 超参数部分
        learn_time = 2  # 梯度下降法次数   这个算法巨慢!! 扩大学习次数要慎重
        train_size = x_train.shape[0]
        batch_size = 100
        learning_rate = 0.1  # 梯度下降法学习率
        network = TwoLayerNet_Numerical(input_size=784, hidden_size=50, output_size=10)

        for i in range(learn_time):  # 更新下降iters_num次
            print(i)
            # 获取mini-batch
            batch_mask = np.random.choice(train_size, batch_size)
            x_batch = x_train[batch_mask]
            t_batch = t_train[batch_mask]
            # 计算grads 更新参数
            grads = network.numerical_gradient(x_batch, t_batch)
            for key in ('w1', 'b1', 'w2', 'b2'):
                network.params[key] -= learning_rate * grads[key]
            loss = network.loss(x_batch, t_batch)
            train_loss_list.append(loss)

        x = range(learn_time)
        plt.plot(x, train_loss_list)
        end_time = time.time()
        run_time = end_time - start_time
        print("本次运行时间:%.2f秒" % run_time)
        print("平均一次学习时长:%.2f秒" % (run_time / learn_time))

        plt.show()
        return

    def tryTwoLayerNet_BackPropagation(self):
        """
            一个简单的二层神经网络
            其中激活函数采用Softmax 初始化权重为Xavier高斯分布  代价函数采用交叉熵误差
            采用模块化神经层
            **求梯度采用反向传播**  下降时采用SGD下降法
            使用mini-batch优化
        """
        start_time = time.time()
        (x_train, t_train), (x_test, t_test) = load_mnist(one_hot_label=True, normalize=True)  # 获取mnist
        train_loss_list = []  # 训练代价函数变化情况记录在列表里
        train_accuracy_list = []
        test_accuracy_list = []  # 训练集和验证集的准确率记录在列表里
        # 超参数部分
        learn_time = 50000  # 梯度下降法次数
        train_size = x_train.shape[0]
        batch_size = 10
        learning_rate = 0.1  # 梯度下降法学习率
        network = TwoLayerNet_BackPropagation(input_size=784, hidden_size=50, output_size=10)
        oneepoch_time = max(train_size // batch_size, 1)

        ### network.gradient_check(x_train,t_train)     # 梯度验证

        for i in range(learn_time):  # 更新下降learn_time次
            print(i)
            # 获取mini-batch
            batch_mask = np.random.choice(train_size, batch_size)
            x_batch = x_train[batch_mask]
            t_batch = t_train[batch_mask]
            # 计算grads 更新参数
            grads = network.gradient(x_batch, t_batch)  # 鸟枪换炮
            """
            for key in ('w1', 'b1', 'w2', 'b2'):
                network.params[key] -= learning_rate * grads[key]
            """
            # SGD下降法
            SGD().update(network.params, grads)
            loss = network.loss(x_batch, t_batch)
            train_loss_list.append(loss)
            if (i % oneepoch_time == 0):  # 差不多循环完一次数据集了 来记录一下准确率
                train_acc = network.accuracy(x_train, t_train)
                test_acc = network.accuracy(x_test, t_test)
                train_accuracy_list.append(train_acc)
                test_accuracy_list.append(test_acc)

        x1 = range(learn_time)
        plt.subplot(1, 2, 1)
        plt.plot(x1, train_loss_list)  # 第一个图像展示代价函数
        x2 = range(train_accuracy_list.__len__())
        plt.subplot(1, 2, 2)
        plt.plot(x2, train_accuracy_list, label="train")  # 第二个函数展示精确度
        plt.plot(x2, test_accuracy_list, label="test")
        end_time = time.time()
        run_time = end_time - start_time
        print("本次运行时间:%.2f秒" % run_time)
        print("平均一次学习时长:%.2f秒" % (run_time / learn_time))

        plt.show()
        return

    def tryMultyLayerNet(self):
        """
            全连接的多层神经网络  主要是书中提供的实现
            Parameters
            ----------
            input_size : 输入大小(MNIST的情况下为784)
            hidden_size_list : 隐藏层的神经元数量的列表(e.g. [100, 100, 100])
            output_size : 输出大小(MNIST的情况下为10)
            activation : 'relu' or 'sigmoid'
            weight_init_std : 指定权重的标准差(e.g. 0.01)
                指定'relu'或'he'的情况下设定“He的初始值”
                指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”
            weight_decay_lambda : Weight Decay(L2范数)的强度


        """
        start_time = time.time()
        (x_train, t_train), (x_test, t_test) = load_mnist(one_hot_label=True, normalize=True)  # 获取mnist
        train_loss_list = []  # 训练代价函数变化情况记录在列表里
        train_accuracy_list = []
        test_accuracy_list = []  # 训练集和验证集的准确率记录在列表里
        # 超参数部分
        learn_time = 50000  # 梯度下降法次数
        train_size = x_train.shape[0]
        batch_size = 100
        learning_rate = 0.1  # 梯度下降法学习率
        network = MultiLayerNet(input_size=784, hidden_size_list=[50 for i in range(4)], output_size=10)
                                        # 这里面的hidden_size_list 就可以设置隐藏层每一层的神经元个数以及隐藏层层数
        oneepoch_time = max(train_size // batch_size, 1)

        ### network.gradient_check(x_train,t_train)     # 梯度验证
        train_acc = test_acc = None
        for i in range(learn_time):  # 更新下降learn_time次
            print(i)
            # 获取mini-batch
            batch_mask = np.random.choice(train_size, batch_size)
            x_batch = x_train[batch_mask]
            t_batch = t_train[batch_mask]
            # 计算grads 更新参数
            grads = network.gradient(x_batch, t_batch)  # 鸟枪换炮
            """
            for key in ('w1', 'b1', 'w2', 'b2'):
                network.params[key] -= learning_rate * grads[key]
            """
            # SGD下降法
            SGD().update(network.params, grads)
            loss = network.loss(x_batch, t_batch)
            train_loss_list.append(loss)
            if (i % oneepoch_time == 0):  # 差不多循环完一次数据集了 来记录一下准确率
                train_acc = network.accuracy(x_train, t_train)
                test_acc = network.accuracy(x_test, t_test)
                train_accuracy_list.append(train_acc)
                test_accuracy_list.append(test_acc)

        x1 = range(learn_time)
        plt.subplot(1, 2, 1)
        plt.plot(x1, train_loss_list)  # 第一个图像展示代价函数
        x2 = range(train_accuracy_list.__len__())
        plt.subplot(1, 2, 2)
        title_str = str(network.hidden_layer_num + 1) + "-Layers-Net LearningMap"
        plt.title(title_str)
        plt.plot(x2, train_accuracy_list, label="train")  # 第二个函数展示精确度
        plt.plot(x2, test_accuracy_list, label="test")
        end_time = time.time()
        run_time = end_time - start_time
        print("本次运行为{}层网络 学习{}次 耗时:{:.2f}秒".format(network.hidden_layer_num + 1, learn_time, run_time))
        print("训练集准确率为{:.2f}% 验证集准确率为{:.2f}%".format((train_acc * 100.0), (test_acc * 100.0)))
        plt.legend()
        plt.show()
        return


# main()主函数部分
Main().tryMultyLayerNet()
内容
  • Git是如何工作的
    Git是如何工作的
    2023-12-12
    Git是如何工作的.首先,必须确定代码的存储位置。 常见的假设是只有两个位置 , 一个位于 Github 等远程服务器上
  • 浅析华为云Astro的5大关键能力技术
    浅析华为云Astro的5大关键能
    2023-12-11
    摘要: 本文以技术方案视角,对华为云Astro低代码平台的一些核心功能进行简要介绍。.背景介绍.低代码开发基于可视化开发
  • 语音平台源码搭建开发之表情功能的实现
    语音平台源码搭建开发之表情功能的
    2023-12-10
    从飞鸽传书到网络聊天时代,人与人之间聊天方式有了极大地改变,也让不管是远在天边,还是近在眼前的朋友或家人,都能实现相当于
  • gitlab ci 集成 eslint/prettier/tsc 做代码审查,并使用 eslint 输出作为显示代码质量
    gitlab ci 集成 esl
    2023-12-09
    前言.想自动化一下公司里代码的部分审查,最初想用 reviewdog 的,但是公司的域名基本都在 VPN 中访问的,gi
  • 基于YOLOv5的目标检测系统详解(附MATLAB GUI版代码)
    基于YOLOv5的目标检测系统详
    2023-12-08
    摘要:本文重点介绍了基于YOLOv5目标检测系统的MATLAB实现,用于智能检测物体种类并记录和保存结果,对各种物体检测
  • OpenGL实现GPU体渲染
    OpenGL实现GPU体渲染
    2023-12-07
    之前完成了利用OpenGL实现GPU体渲染的实验,现在把完成的工作做一个总结。.本实验demo的完成主要参考了《Open
  • Git 小技巧:忽略某些文件的更改
    Git 小技巧:忽略某些文件的更
    2023-12-07
    *以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq
  • 【Unity3D】基于粒子系统实现烟花特效
    【Unity3D】基于粒子系统实
    2023-12-07
    1 需求实现.​ 粒子系统ParticleSystem.中介绍了粒子初始化、粒子发射、发射器形状、渲染器、碰撞、子发射器
  • 架构师日记-如何写的一手好代码
    架构师日记-如何写的一手好代码
    2023-12-06
    作者:京东零售 刘慧卿.一 前言.在日常工作中,我经常听到部分同学抱怨代码质量问题,潜台词是:“除了自己的代码,其他人写
  • 4.10 x64dbg 反汇编功能的封装
    4.10 x64dbg 反汇编功
    2023-12-06
    LyScript.插件提供的反汇编系列函数虽然能够实现基本的反汇编功能,但在实际使用中,可能会遇到一些更为复杂的需求,此
  • 小波去噪算法的简易实现及其扩展(小波锐化、高斯拉普拉斯金字塔去噪及锐化)之二。
    

SSE图像算法优化系列九:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms)
    小波去噪算法的简易实现及其扩展(
    2023-12-06
    上一篇文章谈及了GIMP里实现的小波分解,但是这仅仅是把图像分解为多层的数据,如果快速的获取分解数据以及后续怎么利用这些
  • Qt-FFmpeg开发-视频播放【软解码 + OpenGL显示RGB图像】(3)
    Qt-FFmpeg开发-视频播放
    2023-12-06
    Qt-FFmpeg开发-视频播放【软解码 + OpenGL显示RGB图像】.目录.Qt-FFmpeg开发-视频播放【软解
  • 自主三维GIS引擎笔记-实现三维球045
    自主三维GIS引擎笔记-实现三维
    2023-12-05
    最小GIS迷你地球实现(实现一套最小的三维GIS球体) V1.0.0.0版本.数据加代码比较大(主要是数据,数据有1G多
  • 【短道速滑十一】标准的Gabor滤波器及Log_Gabor滤波器的实现、解析、速度优化及其和Halcon中gen_gabor的比较。
    【短道速滑十一】标准的Gabor
    2023-12-05
    最近有朋友在研究Halcon中gen_gabor的函数,和我探讨,因为我之前也没有怎么去关注这个函数,因此,前前后后大概
  • java代码审计-XSS
    java代码审计-XSS
    2023-12-04
    0x01 前言.XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行
  • SonarQube系列-通过配置扫描分析范围,聚焦关键问题
    SonarQube系列-通过配置
    2023-12-04
    在许多情况下,你可能不希望分析项目中每个源文件的各个方面。例如,项目可能包含生成的代码、库中的源代码或有意复制的代码。在
  • 1.4 编写简易ShellCode弹窗
    1.4 编写简易ShellCod
    2023-12-02
    在前面的章节中相信读者已经学会了使用Metasploit工具生成自己的ShellCode代码片段了,本章将继续深入探索关
  • SonarQube系列-架构与外部集成
    SonarQube系列-架构与外
    2023-12-02
    介绍.Sonar是一个代码质量管理的开源平台,基于Java开发的,用于管理源代码的质量,通过插件形式,可以支持包括jav
  • 驱动开发:内核RIP劫持实现DLL注入
    驱动开发:内核RIP劫持实现DL
    2023-12-01
    本章将探索内核级DLL模块注入实现原理,DLL模块注入在应用层中通常会使用CreateRemoteThread直接开启远
  • 驱动开发:摘除InlineHook内核钩子
    驱动开发:摘除InlineHoo
    2023-12-01
    在笔者上一篇文章《驱动开发:内核层InlineHook挂钩函数》中介绍了通过替换函数头部代码的方式实现Hook挂钩,对于
  • 景观照明工程
    景观照明工程
    2023-12-16
    景观照明工程.产品功能.景观照明工程是一种专门为户外景观设计的照明方案。它既可以美化城市风景,提升城市形象,也可以为人们
  • 排水**设计施工
    排水**设计施工
    2023-12-26
    排水**设计施工.产品功能.我们的排水系*设计施工服务旨在解决建筑物排水问题,确保排水系*的高效运行。我们提供全面的设计
  • 商业区景观规划设计
    商业区景观规划设计
    2024-01-20
    商业区景观规划设计.产品功能.我们的商业区景观规划设计产品旨在为商业区打造出色的室外环境,提升商业区的形象和吸引力。我们
  • 园林景观施工管理
    园林景观施工管理
    2023-12-21
    园林景观施工管理产品介绍.产品功能.园林景观施工管理是一款专业的园林景观施工项目**软件,旨在帮助园林景观施工公司或项目
  • 景观灯具**
    景观灯具**
    2024-01-10
    景观灯具**.产品功能.防水设计,可适应各种恶劣天气.高亮度LED灯源,节能环保.多种灯具款式可选,满足不同景观需求.灯
  • 喷泉景观设计
    喷泉景观设计
    2024-01-05
    喷泉景观设计.喷泉是一种极具观赏性和装饰性的景观设计元素,它不仅可以为周围的环境增添一份生气与动感,更可以为人们带来一份
  • 室外雕塑设计与**
    室外雕塑设计与**
    2023-12-06
    室外雕塑设计与**.产品功能.我们的室外雕塑设计与**服务旨在为客户提供高品质的室外雕塑,满足各种不同场合的需求。我们拥
  • 城市绿化规划设计
    城市绿化规划设计
    2024-01-15
    城市绿化规划设计.随着城市化进程的不断加快,城市绿化规划设计成为了一个备受关注的问题。如何在城市中保护和增加绿地,促进城
  • 绿化工程材料供应
    绿化工程材料供应
    2023-12-16
    绿化工程材料供应.产品功能.我们的绿化工程材料供应主要用于城市绿化、园林景观建设、庭院绿化等相关项目。产品种类丰富,覆盖
  • 市政园林设计
    市政园林设计
    2023-12-11
    市政园林设计.产品功能.我们的市政园林设计旨在为城市和社区提供优美、宜人的户外环境。我们设计的产品涵盖了公园、**、街道