• 背景介绍

    Neural Network之模型复杂度主要取决于优化参数个数与参数变化范围. 优化参数个数可手动调节, 参数变化范围可通过正则化技术加以限制. 本文从优化参数个数出发, 以Residual Block技术为例, 简要演示Residual Block残差块对Neural Network模型复杂度的影响.

  • 算法特征

    ①. 对输入进行等维度变换; ②. 以加法连接前后变换扩大函数空间

  • 算法推导

    典型残差块结构如下,

    即, 输入\(x\)之函数空间通过加法\(x + f(x)\)扩大. 可以看到, 在前向计算过程中, 函数\(f(x)\)之作用类似于残差, 补充输入\(x\)对标准输出描述之不足; 同时, 在反向传播过程中, 对输入\(x\)之梯度计算分裂在不同影响链路上, 降低了函数\(f(x)\)对梯度的直接影响.

  • 数据、模型与损失函数

    数据生成策略如下,

    \[\left\{
    \begin{align*}
    x &= r + 2g + 3b \\
    y &= r^2 + 2g^2 + 3b^2 \\
    lv &= -3r - 4g - 5b
    \end{align*}
    \right.
    \]

    Neural Network网络模型如下,

    其中, 输入层\(x:=(r, g, b)\), 输出层\(y:=(x, y, lv)\), 中间所有隐藏层与输入之dimension保持一致.

    损失函数如下,

    \[L = \sum_{i}\frac{1}{2}(\bar{x}^{(i)} - x^{(i)})^2 + \frac{1}{2}(\bar{y}^{(i)} - y^{(i)})^2 + \frac{1}{2}(\bar{lv}^{(i)} - lv^{(i)})^2
    \]

    其中, \(i\)为data序号, \((\bar{x}, \bar{y}, \bar{lv})\)为相应观测值.

  • 代码实现

    本文以是否采用Residual Block为例(即在上述模型中是否去除\(\oplus\)), 观察Residual Block对模型复杂度的影响.

    code
    import numpy
    import torch
    from torch import nn
    from torch import optim
    from torch.utils import data
    from matplotlib import pyplot as plt numpy.random.seed(0) # 获取数据与封装数据
    def xFunc(r, g, b):
    x = r + 2 * g + 3 * b
    return x def yFunc(r, g, b):
    y = r ** 2 + 2 * g ** 2 + 3 * b ** 2
    return y def lvFunc(r, g, b):
    lv = -3 * r - 4 * g - 5 * b
    return lv class GeneDataset(data.Dataset): def __init__(self, rRange=[-1, 1], gRange=[-1, 1], bRange=[-1, 1], \
    num=100, transform=None, target_transform=None):
    self.__rRange = rRange
    self.__gRange = gRange
    self.__bRange = bRange
    self.__num = num
    self.__transform = transform
    self.__target_transform = target_transform self.__X = self.__build_X()
    self.__Y_ = self.__build_Y_() def __build_Y_(self):
    rArr = self.__X[:, 0:1]
    gArr = self.__X[:, 1:2]
    bArr = self.__X[:, 2:3]
    xArr = xFunc(rArr, gArr, bArr)
    yArr = yFunc(rArr, gArr, bArr)
    lvArr = lvFunc(rArr, gArr, bArr)
    Y_ = numpy.hstack((xArr, yArr, lvArr))
    return Y_ def __build_X(self):
    rArr = numpy.random.uniform(*self.__rRange, (self.__num, 1))
    gArr = numpy.random.uniform(*self.__gRange, (self.__num, 1))
    bArr = numpy.random.uniform(*self.__bRange, (self.__num, 1))
    X = numpy.hstack((rArr, gArr, bArr))
    return X def __len__(self):
    return self.__num def __getitem__(self, idx):
    x = self.__X[idx]
    y_ = self.__Y_[idx]
    if self.__transform:
    x = self.__transform(x)
    if self.__target_transform:
    y_ = self.__target_transform(y_)
    return x, y_ # 构建模型
    class Model(nn.Module): def __init__(self, is_residual_block=True):
    super(Model, self).__init__()
    torch.random.manual_seed(0) self.__is_residual_block = is_residual_block
    self.__in_features = 3
    self.__out_features = 3 self.lin11 = nn.Linear(3, 3, dtype=torch.float64)
    self.lin12 = nn.Linear(3, 3, dtype=torch.float64)
    self.lin21 = nn.Linear(3, 3, dtype=torch.float64)
    self.lin22 = nn.Linear(3, 3, dtype=torch.float64)
    self.lin31 = nn.Linear(3, 3, dtype=torch.float64)
    self.lin32 = nn.Linear(3, 3, dtype=torch.float64) def forward(self, X):
    X1 = self.lin12(torch.tanh(self.lin11(X)))
    if self.__is_residual_block:
    X1 += X
    X1 = torch.tanh(X1) X2 = self.lin22(torch.tanh(self.lin21(X1)))
    if self.__is_residual_block:
    X2 += X1
    X2 = torch.tanh(X2) X3 = self.lin32(torch.tanh(self.lin31(X2)))
    if self.__is_residual_block:
    X3 += X2
    return X3 # 构建损失函数
    class MSE(nn.Module): def forward(self, Y, Y_):
    loss = torch.sum((Y - Y_) ** 2)
    return loss # 训练单元与测试单元
    def train_epoch(trainLoader, model, loss_fn, optimizer):
    model.train(True) loss = 0
    with torch.enable_grad():
    for X, Y_ in trainLoader:
    optimizer.zero_grad() Y = model(X)
    lossVal = loss_fn(Y, Y_)
    lossVal.backward()
    optimizer.step() loss += lossVal.item() loss /= len(trainLoader.dataset)
    return loss def test_epoch(testLoader, model, loss_fn, optimzier):
    model.train(False) loss = 0
    with torch.no_grad():
    for X, Y_ in testLoader:
    Y = model(X)
    lossVal = loss_fn(Y, Y_)
    loss += lossVal.item()
    loss /= len(testLoader.dataset)
    return loss def train_model(trainLoader, testLoader, epochs=100):
    model_RB = Model(True)
    loss_RB = MSE()
    optimizer_RB = optim.Adam(model_RB.parameters(), 0.001) model_No = Model(False)
    loss_No = MSE()
    optimizer_No = optim.Adam(model_No.parameters(), 0.001) trainLoss_RBList = list()
    testLoss_RBList = list()
    trainLoss_NoList = list()
    testLoss_NoList = list()
    for epoch in range(epochs):
    trainLoss_RB = train_epoch(trainLoader, model_RB, loss_RB, optimizer_RB)
    testLoss_RB = test_epoch(testLoader, model_RB, loss_RB, optimizer_RB)
    trainLoss_No = train_epoch(trainLoader, model_No, loss_No, optimizer_No)
    testLoss_No = test_epoch(testLoader, model_No, loss_No, optimizer_No) trainLoss_RBList.append(trainLoss_RB)
    testLoss_RBList.append(testLoss_RB)
    trainLoss_NoList.append(trainLoss_No)
    testLoss_NoList.append(testLoss_No)
    if epoch % 50 == 0:
    print(epoch, trainLoss_RB, trainLoss_No, testLoss_RB, testLoss_No) fig = plt.figure(figsize=(5, 4))
    ax1 = fig.add_subplot(1, 1, 1)
    X = numpy.arange(1, epochs+1)
    ax1.plot(X, trainLoss_RBList, "r-", lw=1, label="train with RB")
    ax1.plot(X, testLoss_RBList, "r--", lw=1, label="test with RB")
    ax1.plot(X, trainLoss_NoList, "b-", lw=1, label="train without RB")
    ax1.plot(X, testLoss_NoList, "b--", lw=1, label="test without RB")
    ax1.set(xlabel="epoch", ylabel="loss", yscale="log")
    ax1.legend()
    fig.tight_layout()
    fig.savefig("loss.png", dpi=300)
    plt.show() if __name__ == "__main__":
    trainData = GeneDataset([-1, 1], [-1, 1], [-1, 1], num=1000, \
    transform=torch.tensor, target_transform=torch.tensor)
    testData = GeneDataset([-1, 1], [-1, 1], [-1, 1], num=300, \
    transform=torch.tensor, target_transform=torch.tensor)
    trainLoader = data.DataLoader(trainData, batch_size=len(trainData), shuffle=False)
    testLoader = data.DataLoader(testData, batch_size=len(testData), shuffle=False)
    epochs = 10000
    train_model(trainLoader, testLoader, epochs)
  • 结果展示

    可以看到, 由于Residual Block结构引入额外的优化参数, 模型复杂度得以提升. 同时, 相较于常规Neural Network(对应去除Residual Block之\(\oplus\)), Residual Block之Neural Network在优化参数个数相同的前提下更加稳妥地扩大了函数空间.

  • 使用建议

    ①. 残差函数之设计应当具备与目标输出匹配之能力;

    ②. 残差函数之设计可改变dimension, 此时\(\oplus\)侧之输入应当进行线性等维调整;

    ③. 若训练数据之复杂度高于测试数据, 则在训练起始, 训练数据之loss可能也要高于测试数据.

  • 参考文档

    ①. 动手学深度学习 - 李牧

Neural Network模型复杂度之Residual Block - Python实现的更多相关文章

  1. 论文翻译:2020_Nonlinear Residual Echo Suppression using a Recurrent Neural Network

    论文地址:https://indico2.conference4me.psnc.pl/event/35/contributions/3367/attachments/779/817/Thu-1-10- ...

  2. Convolutional Neural Network-week2编程题2(Residual Networks)

    1. Residual Networks(残差网络) 残差网络 就是为了解决深网络的难以训练的问题的. In this assignment, you will: Implement the basi ...

  3. 论文翻译:2020_WaveCRN: An efficient convolutional recurrent neural network for end-to-end speech enhancement

    论文地址:用于端到端语音增强的卷积递归神经网络 论文代码:https://github.com/aleXiehta/WaveCRN 引用格式:Hsieh T A, Wang H M, Lu X, et ...

  4. 论文翻译:2020_FLGCNN: A novel fully convolutional neural network for end-to-end monaural speech enhancement with utterance-based objective functions

    论文地址:FLGCNN:一种新颖的全卷积神经网络,用于基于话语的目标函数的端到端单耳语音增强 论文代码:https://github.com/LXP-Never/FLGCCRN(非官方复现) 引用格式 ...

  5. 论文翻译:2022_PACDNN: A phase-aware composite deep neural network for speech enhancement

    论文地址:PACDNN:一种用于语音增强的相位感知复合深度神经网络 引用格式:Hasannezhad M,Yu H,Zhu W P,et al. PACDNN: A phase-aware compo ...

  6. 论文笔记之:Hybrid computing using a neural network with dynamic external memory

    Hybrid computing using a neural network with dynamic external memory Nature  2016 原文链接:http://www.na ...

  7. machine learning 之 Neural Network 3

    整理自Andrew Ng的machine learning课程week6. 目录: Advice for applying machine learning (Decide what to do ne ...

  8. 通过Visualizing Representations来理解Deep Learning、Neural network、以及输入样本自身的高维空间结构

    catalogue . 引言 . Neural Networks Transform Space - 神经网络内部的空间结构 . Understand the data itself by visua ...

  9. Recurrent Neural Network[survey]

    0.引言 我们发现传统的(如前向网络等)非循环的NN都是假设样本之间无依赖关系(至少时间和顺序上是无依赖关系),而许多学习任务却都涉及到处理序列数据,如image captioning,speech ...

  10. 论文笔记系列-Neural Network Search :A Survey

    论文笔记系列-Neural Network Search :A Survey 论文 笔记 NAS automl survey review reinforcement learning Bayesia ...

随机推荐

  1. Jest + React 单元测试最佳实践

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 前言 单元测试是一种用于测试"单元"的软件测试方 ...

  2. Mybatis Plus (特性、快速入门、日志、CRUD)

    Mybatis plus 可以节省很多的工作,所有的CRUD JPA yk-mapper Mybatis plus 偷懒的 简介: MyBatis-Plus(opens new window)(简称 ...

  3. JZOJ 4896. 【NOIP2016提高A组集训第16场11.15】兔子

    题目 在一片草原上有 \(N\) 个兔子窝,每个窝里住着一只兔子,有M条路径连接这些窝.更特殊地是,至多只有一个兔子窝有3条或更多的路径与它相连,其它的兔子窝只有1条或2条路径与其相连.换句话讲,这些 ...

  4. Hexo系列(三):Hexo主题

    作者:独笔孤行 官网:​​ ​http://anyamaze.com​​ 公众号:云实战 Hexo支持更换主题,支持多种主题模式,也支持自定义主题. Hexo主题地址1:https://hexo.io ...

  5. 前后端分离项目创建项目详细过程项、目需求分析、pip换源、创建虚环境、后端目录调整以及解决问题

    引言,本项目是前后端分离的,前端用Vue2 后端用Django,后台管理部分是通过simpleUI完成的项目,项目名称为路飞,是商城类(知识付费项目).本篇文章主要讨论一个前后端分离的项目第一步怎么做 ...

  6. 按highcharts中column形式转对象展现格式

    highcharts图表type:column事例的格式是这样的: (不论接口返回什么格式,需要转换成下面这样的): xAxis: { categories: ['一月','二月'], }, seri ...

  7. refactorObjProps:裁剪、添加对象字段或更新字段内容

    介绍 根据模板,自动对一个 JS 对象的字段进行裁剪.添加或更新字段类型. 比如,做一个设置功能,其设置的数据(对象)存储在 localStorage 中.如果对象的字段名称更新了.或增加了一个新的字 ...

  8. 使用vscode编辑markdown

    目录 markdown在vscode中的使用 标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 列表 图片 表格 网址 代码 文本样式 引用 目录 vscode中使用的插件推荐 截图工 ...

  9. Java项目常用的异常处理

    一.常见异常形式 1.空指针异常(java.lang.nullpointerexception)发生该情况一般是字符串变量未初始化,数组未初始化,类对象未初始化等.还有一种情况是当该对象为空时你并没有 ...

  10. 洛谷 P6021 洪水

    题意 给定一棵有 \(n\) 个结点的树,点有点权:一共有 \(m\) 次操作,每次操作包括以下两种: 在一个点的子树中删去一些结点,使得该子树中所有叶结点与该子树的根结点不连通,并且使删去的点的点权 ...