大家好,又见面了,我是你们的朋友全栈君。
原论文:Residual Dense Network for Image Super-Resolution
数据集 DIV2K
DIV2K中共有1000张2K分辨率图像。其中,训练用图像800张,验证用图像100张,测试用图像100张。
如何从HR(High Resolution,高分辨率,Super Resolution,SR,超分辨率)得到LR(Low Resolution,低分辨率)图像?训练输入LR的图片使用该2k图片通过下面3种处理得到:
- BI方式:主要通过Bicubic下采样得到,缩小比例为x2,x3,x4;
- BD方式:先对原始图片做(7*7卷积,1.6方差)高斯滤波,再对滤波后图片做下采样;
- DN方式:先做Bicubic下采样,再加30%的高斯噪声。
网络结构
输入的是低分辨率图像——LR,输出的是高分辨率图像——HR。
上面的网络称为RDN(Residual Dense Network),其包括4个模块:
- shallow feature extraction net (SFENet)
网络的前2个卷积层 - redidual dense blocks (RDBs)
RDB = Residual Block + Dense Block,是两者的整合。单个RDB的内部结构如下图:
RDB的CM机制(contiguous memory mechanism)其实就是图2中的一个个Conv+ReLU。比如对于第 d d d个RDB,其有 C C C个Conv+ReLU,那么:
对于第1个Conv+ReLU的CM机制是指:
F d , 1 = R e L U ( W d , 1 T ∗ F d − 1 ) F_{d,1}=ReLU(W_{d,1}^T*F_{d-1}) Fd,1=ReLU(Wd,1T∗Fd−1)
对于第2个Conv+ReLU的CM机制是指:
F d , 2 = R e L U ( W d , 2 T ∗ [ F d − 1 , F d , 1 ] ) F_{d,2}=ReLU(W_{d,2}^T*[F_{d-1},F_{d,1}]) Fd,2=ReLU(Wd,2T∗[Fd−1,Fd,1])
对于第 c c c个Conv+ReLU的CM机制是指:
F d , c = R e L U ( W d , c T ∗ [ F d − 1 , F d , 1 , . . . , F d , c − 1 ] ) , 1 ≤ c ≤ C F_{d,c}=ReLU(W_{d,c}^T*[F_{d-1},F_{d,1},…,F_{d,c-1}]), 1 \leq c \leq C Fd,c=ReLU(Wd,cT∗[Fd−1,Fd,1,...,Fd,c−1]),1≤c≤C
local feature fusion (LFF)就是图2中的 F d , L F = H L F F d ( [ F d − 1 , F d , 1 , . . . , F d , c , . . . , F d , C ] ) F_{d,LF}=H_{LFF}^d([F_{d-1},F_{d,1,…,F_{d,c,…,F_{d,C}}}]) Fd,LF=HLFFd([Fd−1,Fd,1,...,Fd,c,...,Fd,C]),也就是Concat+1*1Conv
local residual learning (GRL)就是图2中的 F d = F d − 1 + F d , L F F_d=F_{d-1}+F_{d,LF} Fd=Fd−1+Fd,LF
3者的区别如下图:
- dense feature fusion (DFF)
global feature fusion (GFF)
就是图1中的Concat+1*1Conv部分,即 F G F = H G F F ( [ F 1 , . . . , F D ] ) F_{GF}=H_{GFF}([F_1,…,F_D]) FGF=HGFF([F1,...,FD])
global residual learning (GRL)
就是图1中的 F D F = F − 1 + F G F F_{DF}=F_{-1}+F_{GF} FDF=F−1+FGF
所以DFF = GFF+GRL - up-sampling net (UPNet)
表示RDN网络最后的上采样+卷积操作。实现了输入图片的放大操作。
RDN和DenseNet block的区别:
(1)RDN中的RDB模块去掉了DenseNet每个block中的batchnorm
(2)RDN中的RDB模块去掉了DenseNet每个block中的pooling
(3)DenseNet中每一个dense block的输出都是concat起来的。而RDB将d-1层的特征也和1到d层的特征做了局部特征融合(local feature fusion (LFF)),更好的保证了信息流的贯通。
(4)在整个RDN网络上,每一个RDB模块的输出都会最终被concat起来利用。而DenseNet 整个网络中只使用每一个DenseBlock最后的输出。
==================================
https://blog.csdn.net/qq_14845119/article/details/81459859
RDN和DenseNet block的区别:
(1)RDN中的RDB模块去掉了DenseNet每个block中的batchnorm
(2)RDN中的RDB模块去掉了DenseNet每个block中的pooling
(3)DenseNet中每一个dense block的输出都是concat起来的。而RDB将d-1层的特征也和1到d层的特征做了局部特征融合(local feature fusion (LFF)),更好的保证了信息流的贯通。
(4)在整个RDN网络上,每一个RDB模块的输出都会最终被concat起来利用。而DenseNet 整个网络中只使用每一个DenseBlock最后的输出。
RDN和SRDenseNet 的区别:
(1)RDN通过3个方面改进SRDenseNet 中使用的传统DenseBlock模块。1,加入了contiguous memory (CM) mechanism 使得先前的RDB模块和当前的RDB模块都有直接接触。2,得益于local feature fusion (LFF) ,RDB模块可以容许更大的增长率。3,RDB中Local residual
learning (LRL) 的应用增加了信息和梯度的流动。
(2)RDB内部没有稠密连接。
(3)SRDenseNet 使用L2 loss,RDB使用L1 loss。
RDN和MemNet 的区别:
(1)MemNet 需要对原始图片使用Bicubic插值方式进行上采样,而RDN直接使用原始低分辨图片,优势就是可以减少计算量和提高效果。
(2)MemNet 中包含逆向和门限单元的模块就不再接受先前模块的输入,而RDB各个模块之间是有信息流交互的。
(3)MemNet 没有全部利用中间的特征信息,而RDN通过Global Residual Learning 将所有信息都利用起来。
==================================
实现细节
github实现: 论文作者实现、第三方实现。这里以第三方实现代码进行分析。
构建网络
def model(self):
F_1 = tf.nn.conv2d(self.images, self.weightsS['w_S_1'], strides=[1,1,1,1], padding='SAME') + self.biasesS['b_S_1']
F0 = tf.nn.conv2d(F_1, self.weightsS['w_S_2'], strides=[1,1,1,1], padding='SAME') + self.biasesS['b_S_2']
FD = self.RDBs(F0)
FGF1 = tf.nn.conv2d(FD, self.weightsD['w_D_1'], strides=[1,1,1,1], padding='SAME') + self.biasesD['b_D_1']
FGF2 = tf.nn.conv2d(FGF1, self.weightsD['w_D_2'], strides=[1,1,1,1], padding='SAME') + self.biasesD['b_D_2']
FDF = tf.add(FGF2, F_1)
FU = self.UPN(FDF)
# FU = self.UPN(F_1)
IHR = tf.nn.conv2d(FU, self.weight_final, strides=[1,1,1,1], padding='SAME') + self.bias_final
return IHR
输入的LR图片shape=(32,32);
w_S_1=[ks, ks, self.c_dim, G0]=(3,3,3,64);
w_S_2=[ks, ks, G0, G]=(3,3,64,64)
b_S_1=[G0]=(64,)
b_S_2=[G]=(64,)
w_D_1=[1, 1, G * D, G0]=(1,1,64*16,64)=(1,1,1024,64)
w_D_2=[ks, ks, G0, G0]=(3,3,64,64)
b_D_1=[G0]=(64,)
b_D_2=[G0]=(64,)
这里重点看下RDBs的实现:
def RDBs(self, input_layer):
rdb_concat = list()
rdb_in = input_layer
for i in range(1, self.D+1):
x = rdb_in
for j in range(1, self.C+1):
tmp = tf.nn.conv2d(x, self.weightsR['w_R_%d_%d' %(i, j)], strides=[1,1,1,1], padding='SAME') + self.biasesR['b_R_%d_%d' % (i, j)]
tmp = tf.nn.relu(tmp)
x = tf.concat([x, tmp], axis=3)
x = tf.nn.conv2d(x, self.weightsR['w_R_%d_%d' % (i, self.C+1)], strides=[1,1,1,1], padding='SAME') + self.biasesR['b_R_%d_%d' % (i, self.C+1)]
rdb_in = tf.add(x, rdb_in)
rdb_concat.append(rdb_in)
return tf.concat(rdb_concat, axis=3)
一共有self.D=16个RDB,每一个RDB有self.C=8个卷积层,所有的卷积都是padding=“SAME”,即每一层卷积的输出=输入。RBDs输出的就是图1中的Concat后的结果,其实,RDBs的实现是相当简单的。
再来看看最后的UPNet实现:
def UPN(self, input_layer):
x = tf.nn.conv2d(input_layer, self.weightsU['w_U_1'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_1']
x = tf.nn.relu(x)
x = tf.nn.conv2d(x, self.weightsU['w_U_2'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_2']
x = tf.nn.relu(x)
x = tf.nn.conv2d(x, self.weightsU['w_U_3'], strides=[1,1,1,1], padding='SAME') + self.biasesU['b_U_3']
x = self.PS(x, self.scale)
return x
w_U_1=[5, 5, G0, 64]=[5, 5, 64, 64]
w_U_2=[3, 3, 64, 32]
w_U_3=[3, 3, 32, self.c_dim * self.scale * self.scale ]=(3, 3, 32, 333)=(3, 3, 32, 27)
b_U_1=[64]
b_U_2=[32]
b_U_3=[self.c_dim * self.scale * self.scale]=[27]
此处UPNet的实现和图1优点不同。其次,SFENet中的两个卷积层是没有经过ReLU的,这和图1是一致的。UPNet中的卷积结果要经过ReLU,这个图1是不同的。代码中的UPN只到图1的Upscale部分,图1中Upscale下面的卷积不在上面的UPN代码中,而在model代码中。所以self.PS就对应图1中的Upscale。
构建模型
def build_model(self, images_shape, labels_shape):
self.images = tf.placeholder(tf.float32, images_shape, name='images')
self.labels = tf.placeholder(tf.float32, labels_shape, name='labels')
self.weightsS, self.biasesS = self.SFEParams()
self.weightsR, self.biasesR = self.RDBParams()
self.weightsD, self.biasesD = self.DFFParams()
self.weightsU, self.biasesU = self.UPNParams()
self.weight_final = tf.Variable(tf.random_normal([self.kernel_size, self.kernel_size, self.c_dim, self.c_dim], stddev=np.sqrt(2.0/9/3)), name='w_f')
self.bias_final = tf.Variable(tf.zeros([self.c_dim], name='b_f')),
self.pred = self.model()
# self.loss = tf.reduce_mean(tf.square(self.labels - self.pred))
self.loss = tf.reduce_mean(tf.abs(self.labels - self.pred))
self.summary = tf.summary.scalar('loss', self.loss)
self.model_name = "%s_%s_%s_%s_x%s" % ("rdn", self.D, self.C, self.G, self.scale)
self.saver = tf.train.Saver(max_to_keep=10)
这里的损失函数就是 l 1 l_1 l1损失即 L = 1 n ∑ i = 1 n ∣ y i − H i ∣ L=\frac{1}{n}\sum_{i=1}^n|y_i-H_i| L=n1∑i=1n∣yi−Hi∣。
训练模型
def train(self, config):
print("\nPrepare Data...\n")
data = input_setup(config)
if len(data) == 0:
print("\nCan Not Find Training Data!\n")
return
data_dir = get_data_dir(config.checkpoint_dir, config.is_train, config.scale)
data_num = get_data_num(data_dir)
batch_num = data_num // config.batch_size
images_shape = [None, self.image_size, self.image_size, self.c_dim]
labels_shape = [None, self.image_size * self.scale, self.image_size * self.scale, self.c_dim]
self.build_model(images_shape, labels_shape)
counter = self.load(config.checkpoint_dir, restore=False)
epoch_start = counter // batch_num
batch_start = counter % batch_num
global_step = tf.Variable(counter, trainable=False)
learning_rate = tf.train.exponential_decay(config.learning_rate, global_step, config.lr_decay_steps*batch_num, config.lr_decay_rate, staircase=True)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
learning_step = optimizer.minimize(self.loss, global_step=global_step)
tf.global_variables_initializer().run(session=self.sess)
merged_summary_op = tf.summary.merge_all()
summary_writer = tf.summary.FileWriter((os.path.join(config.checkpoint_dir, self.model_name, "log")), self.sess.graph)
self.load(config.checkpoint_dir, restore=True)
print("\nNow Start Training...\n")
for ep in range(epoch_start, config.epoch):
# Run by batch images
for idx in range(batch_start, batch_num):
batch_images, batch_labels = get_batch(data_dir, data_num, config.batch_size)
counter += 1
_, err, lr = self.sess.run([learning_step, self.loss, learning_rate], feed_dict={
self.images: batch_images, self.labels: batch_labels})
if counter % 10 == 0:
print("Epoch: [%4d], batch: [%6d/%6d], loss: [%.8f], lr: [%.6f], step: [%d]" % ((ep+1), (idx+1), batch_num, err, lr, counter))
if counter % 10000 == 0:
self.save(config.checkpoint_dir, counter)
summary_str = self.sess.run(merged_summary_op, feed_dict={
self.images: batch_images, self.labels: batch_labels})
summary_writer.add_summary(summary_str, counter)
if counter > 0 and counter == batch_num * config.epoch:
self.save(config.checkpoint_dir, counter)
break
summary_writer.close()
1、数据输入
images_shape=(32,32,3)
labels_shape=(96,96,3)
首先要明白输入的样本是什么,已经label是什么,这段代码在utils.py中:
def preprocess(path, scale = 3, eng = None, mdouble = None):
img = imread(path)
label_ = modcrop(img, scale)
if eng is None:
# input_ = cv2.resize(label_, None, fx=1.0/scale, fy=1.0/scale, interpolation=cv2.INTER_CUBIC)
input_ = PIL_resize(label_, 1.0/scale, PIL.Image.BICUBIC)
else:
input_ = np.asarray(eng.imresize(mdouble(label_.tolist()), 1.0/scale, 'bicubic'))
input_ = input_[:, :, ::-1]
label_ = label_[:, :, ::-1]
return input_, label_
数据集下载:http://data.vision.ee.ethz.ch/cvl/DIV2K/DIV2K_train_HR.zip
我们下载的是800张HR图片。我们对这800张HR图片按比例1.0/scale= 1 3 \frac{1}{3} 31进行缩小(即下采样),并采用BICUBIC插值法进行差值,之后得到的就是有锯齿状的LR图片。我们的样本就是原始的HR图片。
2、导入模型
3、计算epoch和batch
4、引入指数衰减的学习率:
decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
5、使用AdamOptimizer进行优化;
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/150443.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...