论文地址: Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network | IEEE Conference Publication | IEEE Xplore

或者: [1609.05158] Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network (arxiv.org)

ESPCN是2016年提出的,是一篇经典的超分辨率重建算法文章,虽然它的效果和现在的文章相比不算好,但是它所提出的Efficient Sub-pixel Convolution,也叫亚像素卷积/子像素卷积为后面网络PSNR的提升做出了很大贡献,关键这个Sub-pixel Convolution比插值,反卷积,反池化这些上采样方法计算量要更少,因此网络的运行速度会有很大提升,如下图所示。

那么接下来看看这个Sub-pixel Convolution的结构,正常情况下,卷积操作会使feature map的高和宽变小,但当 stride=\frac{1}{r}<1 时,可以让卷积后的feature map的高和宽变大,就实现了分辨率的提升也就是超分辨重建,这个操作叫做sub-pixel convolution。

对于sub-pixel convolution,作者将一个H × W的低分辨率输入图像(Low Resolution)作为输入,低分辨率图像特征提取完毕后,生成n1个特征图,然后经过中间一堆操作等,不管有多少,只要到该上采样的时候,在最后一个卷积调整成 r^{2}C 就可以通过Sub-pixel操作将其变为rH x rW的高分辨率图像(High Resolution)。但是其实现过程不是直接通过插值等方式产生这个高分辨率图像,而是通过卷积先得到 r^{2}C 个通道的特征图(特征图大小和输入低分辨率图像一致),然后通过周期筛选(periodic shuffing)的方法得到这个高分辨率的图像,其中r rr为上采样因子(upscaling factor),也就是图像的扩大倍率。

Pytorch复现

sub-pixel convolution这个操作在pytorch里面提供的有接口,只需要调用就可以了。

输入: (n,channels\cdot upscalefactor^{2},height,width)
输出: (n,channels,upscalefactor\cdot height,upscalefactor\cdot width)
比如:

sub= nn.PixelShuffle(4)
input = torch.tensor(1, 4**2, 4, 4)
output = sub(input)
torch.Size为[1, 1, 16, 16]

SRCNN使用sub-pixel convolution

主干网络:

import torch.nn as nn
import torch.nn.init as init
class Net(nn.Module):
    def __init__(self, upscale_factor):
        super(Net, self).__init__()
        self.relu = nn.ReLU()
        self.conv1 = nn.Conv2d(1, 64, kernel_size=5, stride=1, padding=2)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1)
        self.conv3 = nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1)
        self.conv4 = nn.Conv2d(32, upscale_factor ** 2, kernel_size=3, stride=1, padding=1)
        self.pixel_shuffle = nn.PixelShuffle(upscale_factor)
        self._initialize_weights()
    def _initialize_weights(self):
        init.orthogonal_(self.conv1.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv2.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv3.weight, init.calculate_gain('relu'))
        init.orthogonal_(self.conv4.weight)
    def forward(self, x):
        x = self.conv1(x)
        x = self.relu(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.conv3(x)
        x = self.relu(x)
        x = self.conv4(x)
        x = self.pixel_shuffle(x)
        return x
from __future__ import print_function
from math import log10
import torch
import torch.backends.cudnn as cudnn
from SubPixelCNN.model import Net
from progress_bar import progress_bar
class SubPixelTrainer(object):
    def __init__(self, config, training_loader, testing_loader):
        super(SubPixelTrainer, self).__init__()
        self.CUDA = torch.cuda.is_available()
        self.device = torch.device('cuda' if self.CUDA else 'cpu')
        self.model = None
        self.lr = config.lr
        self.nEpochs = config.nEpochs
        self.criterion = None
        self.optimizer = None
        self.scheduler = None
        self.seed = config.seed
        self.upscale_factor = config.upscale_factor
        self.training_loader = training_loader
        self.testing_loader = testing_loader
    def build_model(self):
        self.model = Net(upscale_factor=self.upscale_factor).to(self.device)
        self.criterion = torch.nn.MSELoss()
        torch.manual_seed(self.seed)
        if self.CUDA:
            torch.cuda.manual_seed(self.seed)
            cudnn.benchmark = True
            self.criterion.cuda()
        self.optimizer = torch.optim.Adam(self.model.parameters(), lr=self.lr)
        self.scheduler = torch.optim.lr_scheduler.MultiStepLR(self.optimizer, milestones=[50, 75, 100], gamma=0.5)  # lr decay
    def save(self):
        model_out_path = "model_path.pth"
        torch.save(self.model, model_out_path)
        print("Checkpoint saved to {}".format(model_out_path))
    def train(self):
        self.model.train()
        train_loss = 0
        for batch_num, (data, target) in enumerate(self.training_loader):
            data, target = data.to(self.device), target.to(self.device)
            self.optimizer.zero_grad()
            loss = self.criterion(self.model(data), target)
            train_loss += loss.item()
            loss.backward()
            self.optimizer.step()
            progress_bar(batch_num, len(self.training_loader), 'Loss: %.4f' % (train_loss / (batch_num + 1)))
        print("    Average Loss: {:.4f}".format(train_loss / len(self.training_loader)))
    def test(self):
        self.model.eval()
        avg_psnr = 0
        with torch.no_grad():
            for batch_num, (data, target) in enumerate(self.testing_loader):
                data, target = data.to(self.device), target.to(self.device)
                prediction = self.model(data)
                mse = self.criterion(prediction, target)
                psnr = 10 * log10(1 / mse.item())
                avg_psnr += psnr
                progress_bar(batch_num, len(self.testing_loader), 'PSNR: %.4f' % (avg_psnr / (batch_num + 1)))
        print("    Average PSNR: {:.4f} dB".format(avg_psnr / len(self.testing_loader)))
    def run(self):
        self.build_model()
        for epoch in range(1, self.nEpochs + 1):
            print("\n===> Epoch {} starts:".format(epoch))
            self.train()
            self.test()
            self.scheduler.step(epoch)
            if epoch == self.nEpochs:
                self.save()

EDSR使用sub-pixel convolution

import math
import torch
import torch.nn as nn
class Net(nn.Module):
    def __init__(self, num_channels, base_channel, upscale_factor, num_residuals):
        super(Net, self).__init__()
        self.input_conv = nn.Conv2d(num_channels, base_channel, kernel_size=3, stride=1, padding=1)
        resnet_blocks = []
        for _ in range(num_residuals):
            resnet_blocks.append(ResnetBlock(base_channel, kernel=3, stride=1, padding=1))
        self.residual_layers = nn.Sequential(*resnet_blocks)
        self.mid_conv = nn.Conv2d(base_channel, base_channel, kernel_size=3, stride=1, padding=1)
        upscale = []
        for _ in range(int(math.log2(upscale_factor))):
            upscale.append(PixelShuffleBlock(base_channel, base_channel, upscale_factor=2))
        self.upscale_layers = nn.Sequential(*upscale)
        self.output_conv = nn.Conv2d(base_channel, num_channels, kernel_size=3, stride=1, padding=1)
    def weight_init(self, mean=0.0, std=0.02):
        for m in self._modules:
            normal_init(self._modules[m], mean, std)
    def forward(self, x):
        x = self.input_conv(x)
        residual = x
        x = self.residual_layers(x)
        x = self.mid_conv(x)
        x = torch.add(x, residual)
        x = self.upscale_layers(x)
        x = self.output_conv(x)
        return x
def normal_init(m, mean, std):
    if isinstance(m, nn.ConvTranspose2d) or isinstance(m, nn.Conv2d):
        m.weight.data.normal_(mean, std)
        if m.bias is not None:
            m.bias.data.zero_()
class ResnetBlock(nn.Module):
    def __init__(self, num_channel, kernel=3, stride=1, padding=1):
        super(ResnetBlock, self).__init__()
        self.conv1 = nn.Conv2d(num_channel, num_channel, kernel, stride, padding)
        self.conv2 = nn.Conv2d(num_channel, num_channel, kernel, stride, padding)
        self.bn = nn.BatchNorm2d(num_channel)
        self.activation = nn.ReLU(inplace=True)
    def forward(self, x):
        residual = x
        x = self.bn(self.conv1(x))
        x = self.activation(x)
        x = self.bn(self.conv2(x))
        x = torch.add(x, residual)
        return x
class PixelShuffleBlock(nn.Module):
    def __init__(self, in_channel, out_channel, upscale_factor, kernel=3, stride=1, padding=1):
        super(PixelShuffleBlock, self).__init__()
        self.conv = nn.Conv2d(in_channel, out_channel * upscale_factor ** 2, kernel, stride, padding)
        self.ps = nn.PixelShuffle(upscale_factor)
    def forward(self, x):
        x = self.ps(self.conv(x))
        return x

EDSR这个网络后面得单独写一篇。

论文中给出的结果如下表,我实际跑出来比原文要略低,应该是因为训练不到位和训练数据集不一样的原因,论文的训练数据集是Image,我是用BSD300训练的。

从左到右分别是原图,Bicubic,ESPCN,效果还说的过去。

论文地址:Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network | IEEE Conference Publication | IEEE Xplore
本文来自于个人微博,本文通过几种比较流行的卷积神经网络的结构图,简单的介绍了卷积审计网络的定义。简单来说,卷积(或内积)就是一种先把对应位置相乘然后再把结果相加的运算。(具体含义或者数学公式可以查阅相关资料)如下图就表示卷积的运算过程:(图1)卷积运算一个重要的特点就是,通过卷积运算,可以使原信号特征增强,并且降低噪音.这里以常用的激活函数sigmoid为例:把上述的计算结果269带入此公式,得出f(x)=1如图是一个人工神经元的模型:(图2)对于每一个神经元,都包含以下几部分:x:表示输入w:表示权重θ:表示偏置∑wx:表示卷积(内积)f:表示激活函数o:表示输出对于一个灰度图片(图3)用s
这篇文章推出了一种具有亚像素卷积层结构的SR算法——ESPCN;相比于SRCNN直接对HRHRHR领域像素卷积ESPCN是直接对输入LRLRLR像素做特征提取,在当时来说,可以算是一种提高计算效率的有效途径。 参考文档: Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural NetworkAbstract1 Introduction2 Method2.1.
       Subpixel算法自身是一种插值算法,与常规的双线性或cubic插值算法相比,提升了图像的分辨率和插值质量(SNR),但达不到人脸解析的效果。为了达成该效果,可选取人脸数据集(如celebA),训练阶段配合DCGAN(深度卷积生成对抗网络)算法,对生成器部分做了二次优化,训练完成后取GAN中的生成网络部分做部署。 1. 网络框架  在部署和推理阶段的网络结构如下,网络命名为ES...
总而言之,拾人牙慧而已。 Content1 亚像素的定义1.1 亚像素理解1.2 何谓亚像素?1.3 何谓亚像素精度?2 图像处理中的sub-pixel是什么意思?3 PixelShuffle参考文献 1 亚像素的定义 下面的内容引自1-2 1.1 亚像素理解 在相机成像的过程中,获得的图像数据是将图像进行了离散化的处理,由于感光元件本身的能力限制,到成像面上每个像素只代表附近. 一个正常的反/逆/转置卷积: 如图,将一个 5×55\times55×5 的小图片变成 7×77\times77×7 的大图片。其中白色虚线区域全填0。ESPCN作者认为,这些白色的填0区域为无效信息,并且对求梯度优化有害处。故提出了一种Sub-pixel方法。 图片经过一系列卷积后,得到大小为 H×W×r2H\times W\times r^2H×W×r2 的特征图。
Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network一级目录二级目录三级目录 Sub-Pixel Convolutional Neural Network) https://arxiv.org/pdf/1609.05158.pdf cuda 9.2 python 3.6 github: [https://github.com/leftthomas/ESPCN](https://github.com/leftthomas/ESPCN) 上面github应该是官方代码,但是用的pytorch0.4以前的版本应该是,cuda是8.0,python是2.7
class ChannelAttention(nn.Module): def __init__(self, in_planes, ratio=16): super(ChannelAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.fc1 = n... 1.1简介 论文名称《Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial》 Ledig C , Theis L , Huszar F , et al. Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network[J]. 2016. 该文针对传统超分辨方法中存在的结果过于平滑的问
卷积神经网络(CNN)是一种深度学习模型,在股票预测中可以用来提取时间序列数据的特征并进行预测。在PyTorch框架中,我们可以使用1D卷积神经网络(1DCNN)来进行股票价格的预测。该网络通过卷积层来提取输入序列中的特征,并通过全连接层进行预测。网络的输入是历史股票收盘价(单特征),输出是预测的股票价格。 具体步骤如下: 1. 数据准备:首先,我们需要准备历史股票数据,包括收盘价等特征。我们可以将数据进行归一化处理,确保数据在相同的尺度范围内。 2. 数据划分:将数据集划分为训练集、验证集和测试集。训练集用于模型的训练,验证集用于调整模型的超参数,测试集用于评估模型的性能。 3. 模型构建:使用PyTorch框架构建1DCNN模型。模型包括卷积层、池化层和全连接层。可以设置合适的激活函数和正则化方法来提高模型的性能。 4. 模型训练:使用训练集对模型进行训练,通过优化算法(如梯度下降)更新模型的参数,使模型逐渐拟合训练数据。 5. 模型评估:使用验证集评估模型的性能,可以计算预测结果与真实值之间的误差,比如均方根误差(RMSE)或平均绝对误差(MAE)等指标。 6. 模型预测:使用测试集对模型进行预测,获得未来股票价格的预测结果。