manout's blog

Something about me

一时手贱覆盖了 /etc/sysctl.conf 文件的全部内容,虽然恢复后发现全是注释,还是后怕。
今后要注意对 /etc 下所有配置文件的备份。

sysctl.conf 的恢复

/etc/sysctl.conf 属于 procps 软件包,删除原 sysctl.conf 重新安装 procps 恢复默认 sysctl.conf 配置。
可以使用 sysctl -a 查看当前所有 sysctl 配置信息,写入 /etc/sysctl.conf 中,原理上效果一样,存在部分语法问题。

1
2
3
# remove the bad sysctl.conf so it will restore the origin sysctl.conf file
sudo mv /etc/sysctl.conf /etc/sysctl.conf.bad
sudo apt -o Dpkg::Options::="--force-confmiss" install --reinstall procps

etckeeper

使用 etckeeper 备份 /etc 文件

安装

1
sudo apt install git-core etckeeper

使用

1
sudo etckeeper init

查看 /etc/etckeeper/etckeeper.conf, 将前五行设置为如下

1
2
3
4
5
# The VCS to use.
# VCS="hg"
VCS="git"
# VCS="bzr"
# VCS="darcs"

执行如下命令

1
sudo etckeeper init

理应输出

1
Initialized empty Git repository in /etc/.git/

如果没有输出,执行以下命令再次重试

1
sudo etckeeper uninit

然后第一次提交

1
sudo etckeeper commit "Initial commit"

今后每次修改 /etc 下面的配置文件就需要提交一次以作记录,方便出现问题时回滚, VCS 使用的 git, 回滚方式与 git 相同。

本地编译平台: Linux Ubuntu 16.04
目标编译平台: aarch64
工具链: bazel

编译 ops_test target, 相对路径为 //mace/ops:ops_test

简单步骤

  • 本地使用 aarch64 编译器交叉编译到 aarch64 平台目标文件
  • 将编译输出 push 到开发板上,运行测试

编译命令

bazel 指定 config 目标编译平台为 aarch64_linux

1
bazel build -s --config aarch64_linux --define openmp=true --define opencl=true --define neon=true //mace/ops:ops_test

在配置 ssh 公钥后登录报 agent refused operation 错误。

原因

ssh-agent 已经在运行但是没有找到附带的 keys.
通常是因为更换 ssh-keys 后没有添加到 ssh-agent 引起。

解决办法

将现有私钥添加到验证代理中,执行如下命令。

1
ssh-add

可使用以下命令查看已经添加的所有身份秘钥

1
ssh-add -l

通常 bazel 中的 BUILD 文件的结构如下

  • package description(comment)
  • 所有 load() 语句
  • package 语句
  • rules 和 marcos

package description

包含对 package 的描述

load 语句

bazel 扩展文件以 .bzl 结尾。使用 load 语句导入扩展文件中的符号

1
load("//build_tools/rules:maprule.bzl", "maprule")

这行代码载入 /build_tools/rules/maprule.bzl 文件并且将 maprule 符号添加到环境中。
该命令通常用来载入 rules, functions 或者 constants(string, list etc.)

package 语句

1
package(default_deprecation, default_testonly, default_visibility, feature)
  • default_visibility
    这个 package 中 rules 的可见度
  • default_deprectation
    这个 package 的 rule 的默认 message
  • default_testonly
    设置这个 package 所有 rules 的 testonly 属性的默认值
  • feature
    设置不同的 flag, 影响当前的 BUILD 文件的语义

rules

先记下见到的

  • cc_library
  • cc_binary

忽然发现,未来很多年都会这样度过了

这里记下卷积神经网络的常用算子,并做简单的分类。主要参考抄自知乎

深度神经网络计算

计算层

这部分算子是深度卷积神经网络的核心,用于将输入的神经元激活值与突触连接强度(权重)进行积分求和,得到新的神经元的膜电位。ヽ(*。>Д<)o゜
根据是否滑窗,是否具有时序结构,可分为如下几种算子,其中 FC(全连接层) 是多层感知机(MLP)的基础,Conv 和 FC 是深度卷积神经网络的基础。RNN, GRU 和 LSTM 是带有时序结构的神经网络模型,主要用于非静态图像的场合,例如语音,文字,视频等。

  • Convolution
  • Convolution Transpose
  • Fully Connection
  • RNN
  • LSTM
  • GRU

池化层

池化层主要用于尺度变换,提取高维特征,主要分为三种池化
局部池化,在图像维度上几个相邻的点被缩减为一个输出点,在 Channel 维度上不变。包括平均池化(AveragePool), 最大值(MaxPool), 范数(LpPool)。主要用于图像的尺寸变换。
全局池化,此时一个 Channel 的所有数据点缩为一个点,因此有几个 Channel 就输出几个数据点。此种类型主要用于深度卷积神经网络中卷积部分与 FC 部分的链接。
ROI-Pooling, 主要用于 Faster-RCNN 等检测识别网络中对感兴趣区域进行尺度归一化,从而输入到识别网络进行识别处理。

  • Average Pool
  • Max Pool
  • Lp Pool
  • Global Average Pool
  • Global Lp Pool
  • Global Max Pool
  • Max Roi Pool

批数据归一化层

归一化层作为一个特殊层,可用于数据的归一化,提高神经网络的性能,降低训练时间。对于带有残差的神经网络非常重要。
目前的高性能网络大多带有归一化层,而绝大多数都会采用 Batch Normalization(BN)。BN 的前向操作并不复杂,但是反向比较复杂,因此用于训练的 BN 需要加入更多的子层。
归一化层的计算方式有 Instance Normalation 和 基于范数的归一化。

  • Batch Normalization
  • Lp Normalization
  • Instance Normalization
  • LRN
  • Mean Variance Normalization

数据归一化

将数据进行归一化处理,通常用于输出层的归一化。

  • SoftMax
  • LogSoftMax
  • HardSoft

其他计算层

Dropout 随机扔掉一些通路,可以用于防止过拟合。
Embedding 用于将词转换为高维表达,是文本的预处理的主要步骤。
GRUUnit 是个实验性函数,功能类似于 GRU 的激活层。

  • Dropout
  • Embedding
  • GRUUnit

基础 Tensor 运算

逐元素运算(element-wise)类

终于知道 element-wise 怎么翻译了

这个类别包含了 Tensor 的一些基础运算,由于输出的数据点只跟对应的哪一个输入的数据点有关,因此可以称为 element-wise 运算,这类运算与输入的数据的维度和结构无关,可以等价的认为是一维向量运算的 Tensor 等效表示。
由于输入数据可能是各种维度,也可以是标量,因此这里的操作都是维度兼容的。
一类特殊情况是一个是向量,一个是标量,scalar-tensor 或者 tensor-scalar

And Or Not Xor Max
ArgMax Min ArgMin Greater Less
Equal Abs Add Sub Mul
Div Neg Reciprocal Sqrt Log
Exp Pow Scale Affine Identity
Clip Cast Ceil Floor

Tensor/矩阵处理类

这部分操作是对整个 Tensor 的数据进行的,即输出可能关系到 Tensor 中的不止一个数据。包括求和,求平均,通用矩阵运算(Gemm), 矩阵乘法,图像缩放等。
其中 Gemm 是矩阵处理的通用表达形式, 即 , 其中 A 为 维,B 为 维,C 和 Y 为 维。

  • Sum
  • Mean
  • Gemm
  • MatMul
  • ImageScaler

激活和非线性函数

激活函数提供了神经网络的非线性拟合能力,不同的激活函数具有各自的性能特点。由于 ReLU 简单且性能较好,因此一般图像处理算法采用 ReLU 函数。而 Sigmoid 和 Tanh 在 LSTM/GRU/RNN 中较为常见。这些函数的计算也是 Element-wise 方式,但是公用较为特殊

ReLu Sigmoid Tanh Elu Selu
PRelu SoftPlus Softsign LeakyReLu HardSigmoid
ThresholdedRelu ScaledTanh ParametricSoftPlus

随机数和常数

这些操作用于产生数据,包括正态随机产生,均匀随机产生,常数等。

  • RandomNormal
  • RandomNormalLike
  • RandomUniform
  • RandomUniformLike
  • Constant

Tensor 变换

这部分算子不会改变 Tensor 的数据,只会对数据的位置和维度进行调整

分割组合算子

这部分可以将多个 Tensor 合并为一个,或者将一个拆分为多个。可以用于分组卷积等。

  • Concat
  • Split

索引变换

索引变换包括 Reshape, 矩阵转置,空间维度和 Feature Map 互换等。可以认为是数据排布关系的变化。
Flatten 将输入 Tensor 平展为一维向量,Squeeze 去掉输入 Tensor 中维度为 1 的维,用于压缩 Tensor 的维数。
Flatten 和 Squeeze 可以认为是 ReShape 的特殊情况

  • Transpose
  • SpaceToDepth
  • DepthToSpace
  • Reshape
  • Flatten
  • Squeeze

数据选取

这部分操作可以根据维度参数,边框或者脚标矩阵参数选取 Tensor 的部分数据,或者对 Tensor 的数据进行复制拓展

  • Slice
  • Gather
  • Tile
  • Crop

数据填充

数据填充分为边缘补 0, 常数填充和拷贝。

  • Pad
  • ConstantFill
  • GivenTensorFill

记下 CNN 中 troubleshooting 的常用方法,原文见于链接

分析之前

  • 使用合适的日志和易于理解变量名
    在 tensorflow 中,可以使用 tensorboard 来可视化计算图。
  • 确定网络连接正确
    保证网络之间的输入输出关系正确
  • 使用数据增强技术
    并不总是适用,常用方法有镜像,旋转,增噪。
  • 确保正则化操作不会一直损失函数中的其他因子
    关掉正则化操作,观察函数损失的数量级的顺序,适当的调整正则化操作的权重,确保正则化后函数损失增大
  • 尝试在一个小数据集上过拟合
  • 当在小数据集上过拟合后,寻找合适的学习率
  • 执行梯度检查

函数损失没有提高

  • 确保使用了合适的损失函数并且优化了正确的 tensor
    常用的损失函数可见链接
  • 使用正确的优化器
    常用的优化器可见链接
  • 确保训练的变量正在优化
    使用 tensorflow 的话,可能需要查看 tensorflow 的直方图,或者写一个脚本,计算出在不同训练时期的每个 tensor
  • 调整初始学习率,实现合适的学习率调度
  • 确保没有过拟合

变量没有在训练

  • 确保它被 tensorflow 当做训练变量
  • 确保没有梯度耗散
  • 确保 ReLus 在工作

梯度耗散/梯度爆炸

  • 使用数据增强技术
  • 实现 dropout 层
  • 增强规范化
  • 实现 Batch Normalization 层
  • 实现基于正确性的终止
  • 如果失败,使用一个更小的网络

其他

  • 使用加权损失函数
  • 改变网络的架构
  • 使用更为协调的模型
  • 将 max/average pooling 替换为 strided convolutions
  • 执行超参数的查询
  • 替换随机数种子
  • 如果不成功,需要更多的训练数据

这两天要修复内部框架中 BatchNorm 层在 use_global_statfalse 情况下与 Caffe 输出相差较大的 bug, 根据公式算法实现后仍与 Caffe 有较大差异,研究下 Caffe 中 BatchNorm 的实现,与论文中的四行公式还是有较大出入。

Caffe 中 BatchNorm 的描述

Caffe 关于 BatchNorm 的描述可见此链接
以下是描述 Caffe 中 BatchNorm 层参数的 proto 文件。

1
2
3
4
5
message BatchNormParameter{
optional bool use_global_stats = 1
optional float moving_average_fraction = 2 [default= = .999];
optional float eps = 3 [default = 1e-5];
}

use_global_stat

use_global_stattrue 时,表示使用全局统计量对当前 mini-batch 进行规范化,当为 false 时不使用全局统计量,而是使用当前 mini-batch 的均值和方差。

这次的问题主要是在模型中 use_global_stat 参数为 false 情况下导致的,内部框架没有处理 use_global_statfalse 的情况,默认使用全局统计量规范化输入数据,有团队发现在一些模型中该参数为 false 时效果更好,于是要添加这个功能。

moving_average_fraction

每次迭代中滑动平均的 系数。
越小时会使全局统计量下降更快,会给最近一次算出来的均值最大的权重
每次迭代中都是用当前 batch 的均值 与之前的滑动均值 更新当前的滑动均值。
公式如下

即为模型参数中的 moving_average_fraction 参数。

原先根据论文的公式实现内部框架中 use_global_statfalse 的情况,发现仍与 Caffe 的差值较大。均值的计算方式就是简单的算术平均,看来要改下算法。

eps

为防止除数为 0 加上的扰动量 , 默认为 .

实现

Caffe 中 BatchNorm 的实现可以定位到 layer/batch_norm_layer.cpp 中的 Forward_cpu 方法。
重点关注 mean 和 variance 的计算部分。

均值的计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// compute mean
caffe_cpu_gemv<Dtype>(CblasNoTrans, channels_ * num, spatial_dim,
1. / (num * spatial_dim), bottom_data,
spatial_sum_multiplier_.cpu_data(), 0.,
num_by_chans_.mutable_cpu_data());
caffe_cpu_gemv<Dtype>(CblasTrans, num, channels_, 1.,
num_by_chans_.cpu_data(), batch_sum_multiplier_.cpu_data(), 0.,
mean_.mutable_cpu_data());
// subtract mean
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, num, channels_, 1, 1,
batch_sum_multiplier_.cpu_data(), mean_.cpu_data(), 0.,
num_by_chans_.mutable_cpu_data());
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, channels_ * num,
spatial_dim, 1, -1, num_by_chans_.cpu_data(),
spatial_sum_multiplier_.cpu_data(), 1., top_data);

以上三个 cblas 库的 gemv 分别完成的是


  • 其中由于输入为四维 NCHW 格式,所以这里计算每个 sample 的均值。输出 即为第 n 个 sample 的第 c 个 channel 的均值。
    为全一列向量,这样即完成单个 feature map 的求和。

  • 求各 channel 的均值的和,这里第一个参数为 CblasTrans 即指定第一个矩阵要转置, 即为第 c 个 channel 的在不同 sample 上的均值

方差的计算

1
2
3
4
5
6
7
8
9
caffe_sqr<Dtype>(top[0]->count(), top_data,
temp_.mutable_cpu_data());
caffe_cpu_gemv<Dtype>(CblasNoTrans, channels_ * num, spatial_dim,
1. / (num * spatial_dim), temp_.cpu_data(),
spatial_sum_multiplier_.cpu_data(), 0.,
num_by_chans_.mutable_cpu_data());
caffe_cpu_gemv<Dtype>(CblasTrans, num, channels_, 1.,
num_by_chans_.cpu_data(), batch_sum_multiplier_.cpu_data(),
0., variance_mutable_cpu_data());

以上三个 cblas 函数调用分别完成的是


  • 调用 sqr 计算 element wise 的 square.

  • 计算 channel 的 feature map 的 variance 的和

  • 计算 各 channel 的 simple 的 variance 的和

均值滤波的计算

1
2
3
4
5
6
7
8
9
this->blobs_[2]->mutable_cpu_data()[0] *= moving_average_fraction_;
this->blobs_[2]->mutable_cpu_data()[0] += 1;
caffe_cpu_axpby(mean_.count(), Dtype(1), mean_.cpu_data(),
moving_average_fraction_, this->blobs_[0]->mutable_cpu_data());
int m = bottom[0]->count() / channels_;
Dtype bias_correction_factor = m > 1 ? Dtype(m) / (m - 1) : 1;
caffe_cpu_axpby(variance_.count(), bias_correction_factor,
variance_.cpu_data(), moving_average_fraction,
this->blobs_[1]->mutable);

moving_average_fraction_, 该参数从模型中读取。
多次迭代后 应趋于稳定。

方差的归一化处理

1
2
3
caffe_add_scalar(variance_.count(), eps_, variance_.mutable_cpu_data());
caffe_sqrt(variance_.count(), variance_.cpu_data(),
variance_.mutable_cpu_data());

分析

Caffe 中大量调用 blas 作为底层计算,blas 的函数接口一般都比较复杂,但是掌握其命名规则和函数的公式还是很好理解的。
Caffe 中的 BatchNorm 层只对数据做了归一化处理,计算均值的方法使用了的均值滤波算法,线性变换操作放在随后的 Scale 层,可以从其模型在 netscope 的可视化模型中看出,每一个 BatchNorm 后都跟有 Scale 层。
但是内部框架的 Batchnorm 层则是在 load_param 方法中载入模型时,使用模型中的全局统计量计算出均值和方差后,在 forward 方法中实现对输入数据的线性变换。
如果修复成功 Caffe 的 BatchNorm 的输出应与内部框架的 BatchNorm 的输出相同,但是在他们的这一层的操作不一样的情况下应该如何做到这一点呢。
Caffe 是在每个 channel 的所有 sample 上做均值滤波,但是内部框架是用来做 inference 的,通常只有一个 sample 作为输入,NCHW 中的 N 一般恒为 1, 源码中其实也没有处理输入数据为 3 维以上的实现,其实 use_global_stat 参数根据 Caffe 中的描述也只有在为训练时才会为 false
(╯‵□′)╯︵┻━┻

  • 继续分析
  • (╯‵□′)╯︵┻━┻ leader 说这做不到实时,毙了

模型又双叒叕改了

0%