SuperResolution by VDSR, PerceptualSR, SubpixelConvSR,ESRGAN

以下内容基于本人阅读理解

VDSR: arXiv:1511.04587v2 [cs.CV] 11 Nov 2016

Perceptual Loss SR: arXiv:1609.05158v2 [cs.CV] 23 Sep 2016

Subpixel SR: arXiv:1603.08155v1 [cs.CV] 27 Mar 2016

SRGAN: arXiv:1609.04802v5 [cs.CV] 25 May 2017

ESRGAN: arXiv:1809.00219v2 [cs.CV] 17 Sep 2018

4个改进DeepLearning方法实现超分辨率的代表作。


Abstract

srcnn证明了deep cnn end2end可以实现优于传统方法的超分辨率任务。

本文介绍的4篇论文分别从网络结构、损失函数、upsample层、GAN四种不同角度优化原始的超分辨率网络。


VeryDeepSuperResolution

原始的srcnn只用了3层,fsrcnn用了15层,已经非常难训练,拟合很慢。VDSR加上了残差连接、gradient clipping,实现了20层的网络和极高的learning rate,以及有更大的感知域(41x41)和在同一个网络实现不同upscale factor。

Residual Learning

image-20190313120326694

interpolation到hr分辨率,经过20层Conv后输出r,加上残差连接x,输出SR结果,因此r = y - x。Loss函数为x+r与y的Euclidean distance。

Gradient Clipping

一般的clipping用[−θ, θ],缺点是即使使用更大的lr,梯度也依然限制在[−θ, θ]内。因此提出可以动态clipping的方法:[-θ/lr , θ/lr],这样就可以根据lr的策略动态控制Gradient Clipping的范围。

Multi—Scale

因为网络只负责reconstruction,上采样是用传统方法完成的。因此一个网络就可以实现多种不同的scale。

Result

image-20190313123237150

20层的conv的runtime居然比3层conv的srcnn还快???


PerceptualSR

PSNR(PeakSignalNoiseRatio)是通用的对比图片相似度的标准,与MSE成正比,因此很多CV任务的loss函数都采用了MSE,即输出和Ground Truth的每个像素的MSE。而Perceptual Loss采用了VGG中间某几层的activation output作为loss。在StyleTransfer和SuperResolution都得到了不错的结果。

Feature Reconstruction Loss

image-20190313134211674

其中:φ j (y)是VGG的第j层的activation output。

相当于借用VGG做feature extraction,feat Loss是图片在higher level feature map的EuclideanNorm,而不是像素级别的距离,保存了图片的semantic和空间结构,但是没有考虑到色彩、纹理和形状。

Style Reconstruction Loss

先定义一个gram matrix:

image-20190313141150026

返回一个Cj x Cj的矩阵,计算φ j (y)的c通道和c‘通道对应的elementwise product的和。相当于计算channel wise的covariance。

image-20190313143020157

style Loss是2个图片的GramMatrix差值的FrobeniusNorm

More Specific

image-20190317193438543

论文主要是利用了VGG16作为LossNetwork计算Loss,ImageTransformNet可以套用各种不同的网络。

做StyleTransfer时,需要同时使用ys,y_hat,yc作为LossNet的输入。

image-20190317234812204

做SR任务时,仅将SR作为y_hat,HR作为yc,选用relu2_2层计算FeatureLoss。

Experiment

论文展示了x4和x8,估计在x2和x3上表现不如其他方法。

图片downsample前先进行高斯模糊(σ = 1.0)

卷积层后面接spacial batch normalization

选了VGG16第2层的输出作为φ(y)计算feat Loss,基于srcnn修改

batch size = 4,计算200k步

Adam,lr=1e-3

test时先进行histogram matching处理,再计算psnr(偏高?)

Result

细节和边缘表现优秀

放大会看到部分色块超出原有边界


SubpixelConvSR (ESPCN)

此前SR任务的upsample操作都是通过fractional convolution进行的,此论文提出了新的upsample方法:子像素重排列。

Network Architecture

image-20190318132622948

全卷积,逐层增加通道数

输入通道为c,upscale factor为r的话,最终通道数为c r^2,即输出为[h, w, c r^2]**

Sub-pixel cone layer通过像素重新排列(不需要计算),将feature map转换为**[h *r, w* r, c]**,实现上采样,论文中命名为periodic shuffling operator (PS)。由于这个过程不需要计算,所以速度非常快。

参考SRCNN的3层网络结构,第一层conv(5x5, c, 64) 第二层conv(3x3, 64, 32) 第三层conv(3x3, 32, c*r^2) 第四层subpixel(r)

Experiment

图片分辨率为(17r x 17r),downsample前先进行高斯模糊

激活函数选tanh

训练100轮,lr=0.01,每1轮lr=lr*0.1

用K2训练,91images上训练了3hr,另外一个模型在ImageNet上训练了7天。

PSNR用matlab算(比较准)

Result

如果激活函数跟SRCNN一样用relu,对比数据集分别用91images和ImageNet,结果显示ESPCN在更大的数据集上会有进一步的表现(+0.33),而SRCNN不会(+0.07)

image-20190318151055556

1080p的图片在GPU上超分辨率可以做到0.038s每帧(算是实时?),而SRCNN需要0.435s

Enhanced SuperResolution GAN(ESRGAN)

SRGAN的升级版,用生成对抗的方法实现超分辨率任务。与PerceptualLoss类似,只是这次的LossNetwork是可训练的,结果同样是在PSNR比较低的情况下得到感官效果更优秀的超分辨率图片。并且在超大upscale factor上的表现比PSNR方法的表现更好。

前作SRGAN

提出一个mean-opinion score(MOS),找26个人评分(1~5)然后求均值。

GAN网络:Generator采用ResNet网络,Discriminator采用VGG。

image-20190318173207720

从manifold的角度分析:将patch映射到2维空间,可以发现GAN的结果与真实结果分布比较接近,而以MSE为objective function的结果偏离了真实分布,所以画面偏smooth。

SRGAN Loss

Loss函数采用,典型的GAN的min-max函数(基于binomial的loss,然后训练Discriminator最大化误差,训练Generator最小化误差)

image-20190318175244115

image-20190319124717727

其中:content loss可以选择通常使用的MSE或者基于VGG的perceptual loss,adversarial loss就是Discriminator的输出

image-20190319132712222

论文中说,adversarial loss是可以让生成图片更接近实际图片的分布。

没有采用cross entropy的log[1-D( G(input) )]而是采用了 -log( D( G(input) )

Experiment

在ImageNet中随机选了350k张图片剪裁成96x96的patch。图片先进行高斯模糊,再downsample。LR preprocess为[0,1],HR为[-1,1]。MSE也是在[-1,1]上计算,VGGLoss需要乘以1/12.75

batch size = 16

optimizer: Adam, beta1=0.9

lr = 1e-4 1e6 iterations

选用预训练SRResNet

测试时去掉边缘4个像素(upsample时由于边缘像素的效果比较差)

Result

image-20190319153954384

用MOS计算,SRGAN是最高分。可是PSNR只有29.4dB(set5)和26.02dB(set14)。

与之前的研究结论不一样,论文认为更深的残差网络可以进一步提高效果,但训练时间更长

ESRGAN本体

在SRGAN的基础上:

  1. 提出Res-in-Res Dense Block,更容易训练。移除Batch Norm,改为Res Scaling(将res的output乘以一个scale之后再加input)
  2. 改用RelativisticGAN。
  3. VGGLoss改用activate前的feature map。

SuperResolution by FSRCNN

以下内容基于本人阅读理解

arXiv:1608.00367v1 [cs.CV] 1 Aug 2016

作为卷积网络实现超像素任务的开山鼻祖,虽然已经不是state-of-the-art的网络,但是新的论文还是会把srcnn拿出来diss一轮。


论文简介

srcnn是第一篇用卷积网络做超像素的,效果超过了传统方法,但是计算量大,不能做到实时。fsrcnn基于srcnn,提出了新的网络结构。

srcnn的短板

预处理过程

srcnn在第一步就通过bicubic interpolation将输入图像放大n倍(n为超像素的倍数),因此此后进行的非线性映射过程,计算量以n^2倍提升。

非线性映射过程

非线性映射过程的层数越多,卷积核越大,效果越好,但是计算量也随之增大。

fsrcnn的改进

引入deconvolution层

deconv层作为输出层,这样mapping的过程都在low resolution space完成,计算量以n^2倍减少。

引入shrinking和expending

shrinking减少通道数,expending扩充通道数。因此mapping过程可以叠加多次卷积而计算量也不会很大。

沙漏状的architecture

一堆卷积操作的叠加实现end-to-end的超像素任务

速度很快

比srcnn-ex快40倍,fsrcnn-s可以在cpu上做到实时而且效果不比srcnn差

迁移学习

训练好的网络只需要fintune deconv层就可以实现其他scale factor的sr

fsrcnnArchitecture

Trick

  1. 网络按scale factor = 3训练,收敛后只fintune deconv层即可得到其他scale factor(如2,4)。
  2. Augmenation,缩小了0.9到0.6倍以及旋转,数据集增大了20倍
  3. 输入的图片需要根据scale factor剪裁成10像素左右的patch(约定俗成?)
  4. 论文作者自制了一个数据集:General1000,图片中比较少纯色块的,适合训练sr任务(可是剪出来的patch还是很多色块啊)。训练时用General1000和91images。
  5. BSD500是jpg格式的,不适合做sr
  6. feature extraction用5x5,deconv用9x9
  7. 激活函数用PReLU,据说不会导致dead kernel
  8. 每层的权重初始化采用不同的mean和stdev,weight和bias采用不同的learning rate
  9. 只采用了MSE作为loss函数,目前state-of-the-art是perceptual loss以及adverserial loss

实验结果

result

Conclusion

将convNet应用到超像素任务,并且非常快,结构非常精简,论文简单易懂。

可是源码是陈旧是caffe,重现实验效果花了我1个月(其实是我太渣…)。忽略了源码中各层权重采用了不同的初始化参数,忽略了data需要做patch,忽略了网络是按scale factor = 3训练后再fintune到其他scale factor的。

曾经是state-of-the-art的超像素网络,但是李飞飞的新论文显示,超像素的效果,不完全是基于psnr判断的,提出一个perceptual loss,替换掉MSE的loss function之后,输出的图片psnr较低,但是感官效果更好。

tfRecord 搬运

概述

最新上传的mcnn中有完整的数据读写示例,可以参考。

关于Tensorflow读取数据,官网给出了三种方法:

**供给数据(Feeding)**: 在TensorFlow程序运行的每一步, 让Python代码来供给数据。

从文件读取数据: 在TensorFlow图的起始, 让一个输入管线从文件中读取数据。

预加载数据: 在TensorFlow图中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况)。

对于数据量较小而言,可能一般选择直接将数据加载进内存,然后再分batch输入网络进行训练(tip:使用这种方法时,结合yield 使用更为简洁,大家自己尝试一下吧,我就不赘述了)。但是,如果数据量较大,这样的方法就不适用了,因为太耗内存,所以这时最好使用tensorflow提供的队列queue,也就是第二种方法 从文件读取数据。对于一些特定的读取,比如csv文件格式,官网有相关的描述,在这儿我介绍一种比较通用,高效的读取方法(官网介绍的少),即使用tensorflow内定标准格式——TFRecords

太长不看,直接看源码请猛戳我的github,记得加星哦。

TFRecords

TFRecords其实是一种二进制文件,虽然它不如其他格式好理解,但是它能更好的利用内存,更方便复制和移动,并且不需要单独的标签文件(等会儿就知道为什么了)… …总而言之,这样的文件格式好处多多,所以让我们用起来吧。

TFRecords文件包含了tf.train.Example 协议内存块(protocol buffer)(协议内存块包含了字段 Features)。我们可以写一段代码获取你的数据, 将数据填入到Example协议内存块(protocol buffer),将协议内存块序列化为一个字符串, 并且通过tf.python_io.TFRecordWriter 写入到TFRecords文件。

从TFRecords文件中读取数据, 可以使用tf.TFRecordReadertf.parse_single_example解析器。这个操作可以将Example协议内存块(protocol buffer)解析为张量。

接下来,让我们开始读取数据之旅吧~

生成TFRecords文件

我们使用tf.train.Example来定义我们要填入的数据格式,然后使用tf.python_io.TFRecordWriter来写入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import os
import tensorflow as tf
from PIL import Image

cwd = os.getcwd()

'''
此处我加载的数据目录如下:
0 -- img1.jpg
img2.jpg
img3.jpg
...
1 -- img1.jpg
img2.jpg
...
2 -- ...
这里的0, 1, 2...就是类别,也就是下文中的classes
classes是我根据自己数据类型定义的一个列表,大家可以根据自己的数据情况灵活运用
...
'''
writer = tf.python_io.TFRecordWriter("train.tfrecords")
for index, name in enumerate(classes):
class_path = cwd + name + "/"
for img_name in os.listdir(class_path):
img_path = class_path + img_name
img = Image.open(img_path)
img = img.resize((224, 224))
img_raw = img.tobytes() #将图片转化为原生bytes
example = tf.train.Example(features=tf.train.Features(feature={
"label": tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
}))
writer.write(example.SerializeToString()) #序列化为字符串
writer.close()

关于Example Feature的相关定义和详细内容,我推荐去官网查看相关API。

基本的,一个Example中包含FeaturesFeatures里包含Feature(这里没s)的字典。最后,Feature里包含有一个 FloatList, 或者ByteList,或者Int64List

就这样,我们把相关的信息都存到了一个文件中,所以前面才说不用单独的label文件。而且读取也很方便。

接下来是一个简单的读取小例子:

1
2
3
4
5
6
7
8
for serialized_example in tf.python_io.tf_record_iterator("train.tfrecords"):
example = tf.train.Example()
example.ParseFromString(serialized_example)

image = example.features.feature['image'].bytes_list.value
label = example.features.feature['label'].int64_list.value
# 可以做一些预处理之类的
print image, label

使用队列读取

一旦生成了TFRecords文件,为了高效地读取数据,TF中使用队列(queue)读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def read_and_decode(filename):
#根据文件名生成一个队列
filename_queue = tf.train.string_input_producer([filename])

reader = tf.TFRecordReader()
_, serialized_example = reader.read(filename_queue) #返回文件名和文件
features = tf.parse_single_example(serialized_example,
features={
'label': tf.FixedLenFeature([], tf.int64),
'img_raw' : tf.FixedLenFeature([], tf.string),
})

img = tf.decode_raw(features['img_raw'], tf.uint8)
img = tf.reshape(img, [224, 224, 3])
img = tf.cast(img, tf.float32) * (1. / 255) - 0.5
label = tf.cast(features['label'], tf.int32)

return img, label

之后我们可以在训练的时候这样使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
img, label = read_and_decode("train.tfrecords")

#使用shuffle_batch可以随机打乱输入
img_batch, label_batch = tf.train.shuffle_batch([img, label],
batch_size=30, capacity=2000,
min_after_dequeue=1000)
init = tf.initialize_all_variables()

with tf.Session() as sess:
sess.run(init)
threads = tf.train.start_queue_runners(sess=sess)
for i in range(3):
val, l= sess.run([img_batch, label_batch])
#我们也可以根据需要对val, l进行处理
#l = to_categorical(l, 12)
print(val.shape, l)

至此,tensorflow高效从文件读取数据差不多完结了。

恩?等等…什么叫差不多?对了,还有几个注意事项:

第一,tensorflow里的graph能够记住状态(state),这使得TFRecordReader能够记住tfrecord的位置,并且始终能返回下一个。而这就要求我们在使用之前,必须初始化整个graph,这里我们使用了函数tf.initialize_all_variables()来进行初始化。

第二,tensorflow中的队列和普通的队列差不多,不过它里面的operationtensor都是符号型的(symbolic),在调用sess.run()时才执行。

第三, TFRecordReader会一直弹出队列中文件的名字,直到队列为空。

总结

  1. 生成tfrecord文件
  2. 定义record reader解析tfrecord文件
  3. 构造一个批生成器(batcher
  4. 构建其他的操作
  5. 初始化所有的操作
  6. 启动QueueRunner

NonLocal Neural Network备忘

论文简介

论文由NonLocal mean启发,每个位置的response(响应?)是由全局的权重值求和相关的。NonLocal block设计为可以插入到已有网络结构层与层之间的形式。在视频分类中表现优秀。

intro

对于序列数据(语音),long distance dependency是建立在递归操作上(如LSTM);对于图像,long distance dependency是建立在由多层卷积操作建立的大感受域上的。

卷积和递归都是基于当前邻域,实现long distance dependency必须反复遍历数据。计算方式低效,训练困难,信息不容易实现双向传递。

跟self-attention有点关系,每个局部的输出都考虑了全局的加权平均值(self-attention是在embedding空间中计算的,该论文提出的Transformer模型革了RNN的命)。

我们的网络表现很牛,而且可以作为一个基础模块,插入到已有网络中。

原理

image-20190114133153895

yi为NonLocal mean输出

i为当前计算点index

j为全局所有计算点index

**f(xi,xj)**计算2点之间相似程度(paper提供了4种方式:Gaussian, Embedded Gaussian, Dot Product, Concatenation)

**g(xj)**为j点的response值

C(x)为归一化值,取值为sum_j {f(xi,xj)}

NonLocal Block

image-20190114133936809

yi为NonLocal mean输出

Wz为NL层的可训练权重

xi作为残差连接,将Wz初始化为0时,则可以插入到已有模型中且不影响模型结果。

实验

基于ResNet50的Conv2D和Inflate3D版本,对比插入1、5、10个NonLocal Block之后的表现。

C2D可以直接用ResNet50的预训练权重;I3D可以将卷积核的每片都用2D的版本初始化然后除以t(t为inflate的倍数)

因为Inflate3D计算量比较大,因此每2个resBlock才inflate一个。

Trick

采用Embedded Gaussian计算NonLocal Mean时通道数选为输入通道的一半,再加一层pooling可以进一步降低计算量到1/4

数据画面随机剪裁,从连续的64帧中随机截选32帧作为一组

8GPU,每个GPU计算8组,相当于一个mini-batch有62组数据

训练400k轮,lr=0.01,每150k减少一个数量级

opt用momentum=0.9,weight decay=0.0001,在最后的global pooling layer用0.5的dropout

在每个NonLocal Block的最后一层1x1x1中使用BatchNorm,并且该层权重初始化为0,保证可以插入到任意已训练的网络结构中

实验结果

image-20190114143358883

4种NonLocal Weight计算方法效果接近。

image-20190114143420341

将1个NonLocal Block加到不同位置,效果接近

image-20190114143455820

更深的网络也可以用NonLocal Block进一步提高表现(ResNet101)

NonLocal的提升也不仅仅是因为深度增加了(R50 + 5-block > R101 base)

image-20190114143622712

NonLocal考虑space&time时效果最好(计算量更大?)

image-20190114143709720

对比I3D,减少了计算量,提高了效果

image-20190115221557322

对比C2D、Inflate3D和NonLocal Inflate3D的表现。NonLocal可以与conv3D互补,得到更好表现

image-20190115220819622

用128帧一组数据,效果更好(计算量翻4倍?)基于32帧的训练模型进行训练,lr=0.0025。在更长的数据中,NLI3D相比I3D也仍然有提升。

ML 入门:归一化、标准化和正则化

因为经常混淆Normalization和Regularization两个名词,所以搬运了一篇文章。原文:https://zhuanlan.zhihu.com/p/29957294

0x01 归一化 Normalization

归一化一般是将数据映射到指定的范围,用于去除不同维度数据的量纲以及量纲单位。

常见的映射范围有 [0, 1] 和 [-1, 1] ,最常见的归一化方法就是 Min-Max 归一化

Min-Max 归一化

x_{new}=\frac{x-x_{min}}{x_{max}-x_{min}}

举个例子,我们判断一个人的身体状况是否健康,那么我们会采集人体的很多指标,比如说:身高、体重、红细胞数量、白细胞数量等。

一个人身高 180cm,体重 70kg,白细胞计数7.50×10^{9}/L,etc.

衡量两个人的状况时,白细胞计数就会起到主导作用从而遮盖住其他的特征,归一化后就不会有这样的问题。

0x02 标准化 Normalization

在这里我们需要强调一下英文翻译的问题,在 Udacity 字幕组中对此进行了探讨:

归一化和标准化的英文翻译是一致的,但是根据其用途(或公式)的不同去理解(或翻译)

下面我们将探讨最常见的标准化方法: Z-Score 标准化

Z-Score 标准化

x_{new}=\frac{x-\mu }{\sigma }

其中\mu是样本数据的均值(mean)\sigma是样本数据的标准差(std)

img

上图则是一个散点序列的标准化过程:原图->减去均值->除以标准差。

显而易见,变成了一个均值为 0 ,方差为 1 的分布,下图通过 Cost 函数让我们更好的理解标准化的作用。

img

机器学习的目标无非就是不断优化损失函数,使其值最小。在上图中,J(w,b)就是我们要优化的目标函数

我们不难看出,标准化后可以更加容易地得出最优参数wb以及计算出J(w,b)的最小值,从而达到加速收敛的效果。[^{[1]}](http://www.zhihu.com/equation?tex=%5E%7B%5B1%5D%7D)

注:上图来源于 Andrew Ng 的课程讲义

Batch Normalization

在机器学习中,最常用标准化的地方莫过于神经网络的 BN 层(Batch Normalization),因此我们简单的谈谈 BN 层的原理和作用,想要更深入的了解可以查看论文

我们知道数据预处理做标准化可以加速收敛,同理,在神经网络使用标准化也可以加速收敛,而且还有如下好处:

  • 具有正则化的效果(Batch Normalization reglarizes the model)
  • 提高模型的泛化能力(Be advantageous to the generalization of network)
  • 允许更高的学习速率从而加速收敛(Batch Normalization enables higher learning rates)

其原理是利用正则化减少内部相关变量分布的偏移(Reducing Internal Covariate Shift),从而提高了算法的鲁棒性。[^{[2]}](http://www.zhihu.com/equation?tex=%5E%7B%5B2%5D%7D)

Batch Normalization 由两部分组成,第一部分是缩放与平移(scale and shift),第二部分是训练缩放尺度和平移的参数(train a BN Network),算法步骤如下:

img

接下来训练 BN 层参数\gamma\beta ,限于篇幅的原因按下不表,有兴趣的读者可以拜读这篇论文

0x03 正则化 Regularization

正则化主要用于避免过拟合的产生和减少网络误差。

正则化一般具有如下形式:

J(http://www.zhihu.com/equation?tex=J%28w%2Cb%29%3D+%5Cfrac%7B1%7D%7Bm%7D+%5Csum_%7Bi%3D1%7D%5E%7Bm%7DL%28f%28x%29%2Cy%29%2B%5Clambda+R%28f%29)= \frac{1}{m} \sum_{i=1}^{m}L(f(x),y)+\lambda R(f)

其中,第 1 项是经验风险,第 2 项是正则项λ≥0为调整两者之间关系的系数。

第 1 项的经验风险较小的模型可能较复杂(有多个非零参数),这时第 2 项的模型复杂度会较大。

常见的有正则项有 L1 正则L2 正则 ,其中 L2 正则 的控制过拟合的效果比 L1 正则 的好。

正则化的作用是选择经验风险与模型复杂度同时较小的模型。[^{[3]}](http://www.zhihu.com/equation?tex=%5E%7B%5B3%5D%7D)

常见的有正则项有 L1 正则L2 正则 以及 Dropout ,其中 L2 正则 的控制过拟合的效果比 L1 正则 的好。

L_{p}范数

为什么叫 L1 正则,有 L1、L2 正则 那么有没有 L3、L4 之类的呢?

首先我们补一补课,L_{p}正则的 L 是指L_{p}范数,其定义为:

L_{0}范数:\left \| w \right \|_{0} = \#(i)\ with \ x_{i} \neq 0(非零元素的个数)

L_{1}范数:\left \| w \right \|_{1} = \sum_{i = 1}^{d}\lvert x_i\rvert(每个元素绝对值之和)

L_{2}范数:\left \| w \right \|_{2} = \Bigl(\sum_{i = 1}^{d} x_i^2\Bigr)^{1/2}(欧氏距离)

L_{p}范数:\left \| w \right \|_{p} = \Bigl(\sum_{i = 1}^{d} x_i^p\Bigr)^{1/p}

在机器学习中,若使用了\lVert w\rVert_p作为正则项,我们则说该机器学习任务引入了L_{p}正则项

img

上图来自周志华老师的《机器学习》插图

L1 正则 Lasso regularizer

J(w,b)=\frac{1}{m} \sum_{i=1}^{m}L(\hat{y},y)+\frac{\lambda }{m}\left \| w \right \|_{1}

  • 凸函数,不是处处可微分
  • 得到的是稀疏解(最优解常出现在顶点上,且顶点上的 w 只有很少的元素是非零的)

L2 正则 Ridge Regularizer / Weight Decay

J(w,b)=\frac{1}{m} \sum_{i=1}^{m}L(\hat{y},y)+\frac{\lambda }{2m}\left \| w \right \|^{2}_{2}

  • 凸函数,处处可微分
  • 易于优化

Dropout

Dropout 主要用于神经网络,其原理是使神经网络中的某些神经元随机失活,让模型不过度依赖某一神经元,达到增强模型鲁棒性以及控制过拟合的效果。

除此之外,Dropout 还有多模型投票等功能,若有兴趣可以拜读这篇论文

img

0x04 Reference

[1] LeCun, Y., Bottou, L., Orr, G., and Muller, K. Efficient backprop. In Orr, G. and K., Muller (eds.), Neural Net-works: Tricks of the trade. Springer, 1998b.

[2] Sergey Ioffe, Christian Szegedy, “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift”, arXiv preprint arXiv:1502.03167, 2015.

[3] 李航. 统计方法学 P13-14

[4] 聊聊机器学习中的损失函数 http://kubicode.me/2016/04/11/Machine%20Learning/Say-About-Loss-Function/

[5] Nitish Srivastava, Geoffrey Hinton, Alex Krizhevsky, Ilya Sutskever and Ruslan Salakhutdinov, “Dropout: A Simple Way to Prevent Neural Networks from
Overfitting”,

量子计算简述 - intro

以下内容基于课堂理解

量子计算发展史

1982年,物理学家Feynman提出一个抽象模型,示范利用量子系统做运算。

1985年,牛津大学的Deutsch提出了第一个量子计算机的设计蓝图和网络模型,定义了量子图灵机。

1994年,Shor证明了大数质因子分解的问题,可以用量子计算机快速解决。

1995年,Grover提出在一个无序的数据库中搜索的问题,可以用量子计算机快速解决。

2001年,IBM使用7个量子比特的计算器,用Shor算法实现15分解成3x5.

2018年,IBM实现50量子比特的处理器原型。掌握50量子比特计算机成熟技术的国家将实现“量子霸权”(Quantum Computational Supremacy)。

量子位和量子寄存器

相较于传统计算机,量子机定义了“量子位”(Qubit)。Qubit可以像传统计算机一样处于“0”态或“1”态,也可以处于叠加态。在量子力学中用狄拉克符号表示一个Qubit:
$$
| \psi \rangle = a | 0 \rangle + b | 1 \rangle
$$
其中:|0>与|1>为基本态;a,b为概率幅,且有:

|a|2+|b|2=1|a|2+|b|2=1

对于2个Qubit组成的希尔伯特(Hilbert)空间,会具有4个基本态,其叠加态为:
$$
| \psi \rangle = a_{00} |00 \rangle + a_{01} |01 \rangle + a_{10} |10 \rangle + a_{11} |11 \rangle
$$
其中各参数定义与单Qubit情况类似。

推广到N位Qubit可知,将具有2^n种相互正交的基本态,因此量子寄存器位数的线性增长将实现量子寄存器容量的指数增长

量子逻辑门

个人理解,Qubit的计算可以参考线性代数。

对量子寄存器叠加态进行变换(幺正变换)以实现逻辑功能的操作,称为量子逻辑门

常见的量子逻辑门包括:CNOT门、Hadamard门、Pauli X/Y/Z门、相位门、π/8门、量子旋转门

叠加态的量子比特在多维空间上进行线性变换(量子学称为幺正变换):将叠加态左乘一个幺正矩阵(量子逻辑门),可以同时对叠加态中所有Qubit进行变换,因此量子计算具有并行性。即对量子寄存器进行一次幺正变换,相当于传统计算机进行2^n次运算。

而传统计算机由于部分计算是串行的,因此不可能通过无限增加处理器数量达到100%并行。

量子测量与输出

对叠加态量子体系的测量将造成波函数的坍缩,使叠加态转化为基态。即n位的量子寄存器叠加保存了2^n个二进制数,但输出结果只能为n位的二进制数。

如n=2的情况:
$$
| \psi \rangle = a_{00} |00 \rangle + a_{01} |01 \rangle + a_{10} |10 \rangle + a_{11} |11 \rangle
$$
测量后,量子体系将会坍缩到|00>、|01>、|10>、|11>四种状态之一,且坍缩的过程是随机的。因此量子计算的目的就是设计量子算法(通过幺正变换),使所需要的结果的概率尽可能大,不需要的结果的概率尽可能小。

对于多量子体系,各个量子的测量是相对独立的,且符合统计原理的。

例如,对第一个Qubit的测量结果为0的概率为:

P|00⟩+|01⟩=|a00|2+|a01|2P|00⟩+|01⟩=|a00|2+|a01|2

若测量得知第一个Qubit结果为0,此时测量第二个Qubit概率分布应为:

P|00⟩=a00|a00|2+|a01|2

MobileNet概览

以下内容基于本人阅读理解

arXiv:1704.04861v1 [cs.CV] 17 Apr 2017

arXiv:1801.04381v3 [cs.CV] 2 Apr 2018

论文简介

谷歌对于目前网络结构为了追求准确率,而不断变深变大,牺牲运算速度的情况不爽。考虑到移动设备和嵌入式设备的运算能力,优化了网络结构,并提出两个超参数width multiplier和resolution multiplier以平衡调节准确率和运算速度的问题。

mobilenetV2中提出新的网络结构inverted residual block。

网络结构

通过优化网络结构,将mobile net的运算量减少到传统卷积网络的1/9,参数量减少到1/7。

下图为mobile net的“深度可分离卷积”:

image-20180912235700174

通过将传统的卷积,分解为 对单个通道的卷积后叠加(而非相加)再执行1x1卷积,减少参数量同时多增加一次batchnorm和relu运算,增加非线性。

并不是稀疏矩阵就一定会比稠密矩阵运算快,但MobileNet将大部分运算集中在1x1卷积中(95%),可以直接调用General Matrix Multiply Funciton(GEMM),反正就是很快。

新增两个超参数

Width multiplier

相当于压缩depthwise seperable Conv的kernel数,可以压缩计算过程中的通道数。

Resolution multiplier

压缩feature map的尺寸,可以减少每层的输入尺寸,计算量以平方级别减少。

MobileNetV2

文章引入了manifold的概念,认为高维度空间的信息是可以被映射(encode)到低维度空间的。

V1就是利用较少的通道数,让空间复杂度降低。width multiplier可以调整feature map的空间维度,让manifold充满(saturate?sapn?)feature map。但是这个方法有时候会失效:对一个1维直线使用ReLU,会输出一条2维的射线;对于n维空间,就会产生有n个结点的曲线。(不确定理解是否有误)

2个可以确定高维度activation space可以映射到低维度子空间的条件:

  1. activation space经过RuLU的非线性变换之后仍为非零,则这个变换存在一个对应的线性变换。
  2. 仅当activation space可以映射到低维度的时候,ReLU才可能保留input space的所有信息。

Inverted Residual Block

InvertedResidualBlock

residual connection是为了优化多层网络时的梯度

  • 中间层深度为0时,这个block就是identity function
  • 中间层深度为input深度时,这个block就是传统的resBlock
  • 中间层深度大于input深度是,效果最好(更高维度的特征表示,更稀疏,耦合更少,更好训练?)

V2的论文同样引入一个新的hyper parameter用于调节中间层深度与输入层深度的ratio,试验结果同样是非常快,非常准。

Conclusions

以上为个人理解总结,部分结论不一定正确。

欢迎指出我的错误。

PyTorch 常用api (不定期更新)

Checkpoint & Loading

1
2
3
4
5
6
# 保存和加载整个模型 
torch.save(model_object, 'model.pkl')
model = torch.load('model.pkl')
# 仅保存和加载模型参数(推荐使用)
torch.save(model_object.state_dict(), 'params.pkl')
model_object.load_state_dict(torch.load('params.pkl'))

Initialization

参数的初始化其实就是对参数赋值。而我们需要学习的参数其实都是Variable,它其实是对Tensor的封装,同时提供了data,grad等接口,这就意味着我们可以直接对这些参数进行操作赋值了。这就是PyTorch简洁高效所在。

所以我们可以进行如下操作进行初始化,当然其实有其他的方法,但是这种方法是PyTorch作者所推崇的:

1
2
3
4
5
6
7
8
9
def weight_init(m):
# 使用isinstance来判断m属于什么类型
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
# m中的weight,bias其实都是Variable,为了能学习参数以及后向传播
m.weight.data.fill_(1)
m.bias.data.zero_()

另一种常见的初始化方法:

1
2
nn.init.kaiming_normal(self.W.weight)
nn.init.constant(self.W[0].bias, 0)

Optimization

局部微调

有时候我们加载了训练模型后,只想调节最后的几层,其他层不训练。其实不训练也就意味着不进行梯度计算,PyTorch中提供的requires_grad使得对训练的控制变得非常简单。

1
2
3
4
5
6
7
8
9
model = torchvision.models.resnet18(pretrained=True)
for param in model.parameters():
param.requires_grad = False
# 替换最后的全连接层, 改为训练100类
# 新构造的模块的参数默认requires_grad为True
model.fc = nn.Linear(512, 100)

# 只优化最后的分类层
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)

全局微调

有时候我们需要对全局都进行finetune,只不过我们希望改换过的层和其他层的学习速率不一样,这时候我们可以把其他层和新层在optimizer中单独赋予不同的学习速率。比如:

1
2
3
4
5
6
7
8
ignored_params = list(map(id, model.fc.parameters()))
base_params = filter(lambda p: id(p) not in ignored_params,
model.parameters())

optimizer = torch.optim.SGD([
{'params': base_params},
{'params': model.fc.parameters(), 'lr': 1e-2}
], lr=1e-3, momentum=0.9)

其中base_params使用1e-3来训练,model.fc.parameters使用1e-2来训练,momentum是二者共有的。

利用Google Cloud Platform的1年免费期搭建私人梯子

Google Cloud Platform(以下简称GCP)是类似于AWS的云服务器平台,提供托管、计算、储存、人工智能api等服务。GCP新用户注册一年或300usd的礼包,且具有台湾地区服务器,ping在40ms以内,与国内dns延迟相当。下面介绍如何快速搭建。


注册Gmail Account

首先你要有梯子,注册一个gmail账号,然后才能开始搭建梯子。(滑稽

注册GCP

  1. 打开网页 https://cloud.google.com/free/ ,选择免费试用GCP

    img

  2. 同意条款 - 填写个人信息、地址(随意) - 填写visa/master信用卡信息.

    信用卡用于支付服务器费用,并会尝试扣1usd验证,然后退还。由于有首年或300usd的新用户礼包,因此第一年不花钱。

  3. 注册后检查自己是否获得新用户礼包。

    1. 打开控制台的结算页面概览:

      image-20180714185147878

    2. 或者,右上角出现礼物盒图标:

      image-20180714185239439

开始部署服务器

  1. 创建实例:

    选择compute engine的vm实例

    image-20180714185720274

  2. 配置实例:

    只做梯子,选低配的实例即可

    image-20180714185937988

    启动磁盘更改一下系统,选择centOS:image-20180714190113826

    防火墙配置:

    image-20180714190557536

    创建后等待片刻就看到运行状态了:

    image-20180714190826609

网络设置

  1. 打开VPC网络设置,将刚刚创建的实例的外部IP设为静态,避免服务器重启后IP改变导致梯子失效。

image-20180714191138428

  1. 设置防火墙规则,允许外部主机访问:

    1. 流量方向入站、来源ip地址0.0.0.0/0、协议和端口全部允许

    2. 流量方向出站、来源ip地址0.0.0.0/0、协议和端口全部允许

      (要创建两次防火墙规则,一次出站,一次入站)

      image-20180714191626974

在实例中安装SS服务

  1. 返回compute engine页面,用ssh通道操作实例:

image-20180714191954536

  1. 获取root权限

    1
    $ sudo su
  2. 安装SS:

    1
    2
    $ wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocksR.sh
    chmod +x shadowsocksR.sh
  3. 配置ss服务器信息:

    1
    $ ./shadowsocksR.sh 2>&1 | tee shadowsocksR.log

    服务器端口:建议为 8989
    密码:自定义
    加密方式:建议改为chacha20
    协议(Protocol):默认为 origin
    混淆(obfs):默认为 plain

  4. 返回compute engine重置VM实例(即重启,不是停止或删除)

至此,服务器及梯子服务已经搭建完成。

在shadowsocks客户端连接梯子

服务器地址即实例外部IP地址:image-20180714192856045

密码、端口、加密方式均按以上自定义内容输入


参考:

shadowsocks官网

google cloud官网

在macOS平台搭建基于Github Page的Hexo博客

以下内容基于网络搬运及个人踩坑总结

环境配置

  1. 前端工具Node.js - 用于生成静态页面

    1
    $ brew install nod

    没有homebrew的话在Terminal键入:

    1
    $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/in stall)"
  2. 前端工具npm - Node Package Manager

    1
    $ npm install
  3. Git
    在Mac Store安装Xcode自带Git工具

    otherwise, visit https://git-scm.com/download

  4. Hexo - 博客框架,一键生成静态页

    1
    $ sudo npm install -g hexo

初始化

  1. 建立hexo目录,执行hero init命令:

  2. $ hexo init <folder>
    
    1
    2
    3
    4
    5

    hexo将自动下载框架至`folder`。

    初始化后`folder`中内容:

    _config.yml db.json node_modules package.json scaffolds source themes
    1
    2
    3

    1. 开启hexo服务器:

    $ hexo server
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    浏览器中打开网址[http://localhost:4000](http://0.0.0.0:4000/),能看到hexo的hello world,则本地环境搭建成功。

    [![img](https://s2.ax1x.com/2019/05/12/E4nX6S.jpg)](https://s2.ax1x.com/2019/05/12/E4nX6S.jpg)

    ## 关联Github

    1. 创建新的Github Repository作为github page的仓库:

    命名规则为:自定义.github.io

    [![img](https://s2.ax1x.com/2019/05/12/E4njOg.jpg)](https://s2.ax1x.com/2019/05/12/E4njOg.jpg)

    2. 打开`folder`中`_config.yml` 编辑最后一行:

    deploy: type: git repo: https://github.com/user_name/自定义.github.io.git branch: master
    1
    2
    3

    1. 用hexo生成静态页:

    $ hexo generate
    1
    2
    3

    2. 部署网页:

    $ hexo deploy
    1
    2
    3

    若deploy出错,可能是未安装hexo deployer git

    $ npm install hexo-deployer-git --save
    1
    2
    3

    deploy成功会显示:

    INFO Deploy done: git
    1
    2
    3

    1. 首次运行要输入github账户密码:

    Username for 'https://github.com': Password for 'https://github.com':
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    2. deploy执行后会将`folder`文件同步至Git Repo,根据网络状况可能上传失败,建议打开vpn。

    上传成功后打开`https://自定义.github.io`即可看到和打开`https://localhost:400`一样的Hello World。

    ## 安装hexo主题

    1. **[hexo官网主题页](https://hexo.io/themes/)** 有大量第三方主题。下面以**cactus**为例。

    2. 在`folder`目录下执行:

    $ git clone https://github.com/probberechts/hexo-theme-cactus.git themes/cactus
    1
    2
    3
    4
    5

    即可将主题下载到`themes`目录下`cactus`文件夹。

    3. 修改`folder`中`_config.yml`:

    # theme: landscape theme: cactus
  3. 保存_config.yml后重新执行hexo generatehexo deploy即可将新主题的静态页部署在git上。