经典论文复现 | 基于深度卷积网络的图像超分辨率算法
過(guò)去幾年發(fā)表于各大 AI 頂會(huì)論文提出的 400 多種算法中,公開(kāi)算法代碼的僅占 6%,其中三分之一的論文作者分享了測(cè)試數(shù)據(jù),約 54% 的分享包含“偽代碼”。這是今年 AAAI 會(huì)議上一個(gè)嚴(yán)峻的報(bào)告。?人工智能這個(gè)蓬勃發(fā)展的領(lǐng)域正面臨著實(shí)驗(yàn)重現(xiàn)的危機(jī),就像實(shí)驗(yàn)重現(xiàn)問(wèn)題過(guò)去十年來(lái)一直困擾著心理學(xué)、醫(yī)學(xué)以及其他領(lǐng)域一樣。最根本的問(wèn)題是研究人員通常不共享他們的源代碼。?
可驗(yàn)證的知識(shí)是科學(xué)的基礎(chǔ),它事關(guān)理解。隨著人工智能領(lǐng)域的發(fā)展,打破不可復(fù)現(xiàn)性將是必要的。為此,PaperWeekly 聯(lián)手百度 PaddlePaddle 共同發(fā)起了本次論文有獎(jiǎng)復(fù)現(xiàn),我們希望和來(lái)自學(xué)界、工業(yè)界的研究者一起接力,為 AI 行業(yè)帶來(lái)良性循環(huán)。
作者丨張榮成?
學(xué)校丨哈爾濱工業(yè)大學(xué)(深圳)
研究方向丨計(jì)算數(shù)學(xué)
筆者本次選擇復(fù)現(xiàn)的是湯曉鷗教授和何愷明團(tuán)隊(duì)發(fā)表于 2015 年的經(jīng)典論文——SRCNN。超分辨率技術(shù)(Super-Resolution)是指從觀測(cè)到的低分辨率圖像重建出相應(yīng)的高分辨率圖像,在監(jiān)控設(shè)備、衛(wèi)星圖像和醫(yī)學(xué)影像等領(lǐng)域都有重要的應(yīng)用價(jià)值。在深度卷積網(wǎng)絡(luò)的浪潮下,本文首次提出了基于深度卷積網(wǎng)絡(luò)的端到端超分辨率算法。
論文復(fù)現(xiàn)代碼:?
http://aistudio.baidu.com/aistudio/#/projectdetail/24446
SRCNN流程
▲?SRCNN算法框架
SRCNN 將深度學(xué)習(xí)與傳統(tǒng)稀疏編碼之間的關(guān)系作為依據(jù),將 3 層網(wǎng)絡(luò)劃分為圖像塊提取(Patch extraction and representation)、非線性映射(Non-linear mapping)以及最終的重建(Reconstruction)。
SRCNN 具體流程如下:?
1.?先將低分辨率圖像使用雙三次差值放大至目標(biāo)尺寸(如放大至 2 倍、3 倍、4 倍),此時(shí)仍然稱放大至目標(biāo)尺寸后的圖像為低分辨率圖像(Low-resolution image),即圖中的輸入(input);?
2. 將低分辨率圖像輸入三層卷積神經(jīng)網(wǎng)絡(luò)。舉例:在論文其中一個(gè)實(shí)驗(yàn)相關(guān)設(shè)置,對(duì) YCrCb 顏色空間中的 Y 通道進(jìn)行重建,網(wǎng)絡(luò)形式為 (conv1+relu1)—(conv2+relu2)—(conv3+relu3);第一層卷積:卷積核尺寸 9×9 (f1×f1),卷積核數(shù)目 64 (n1),輸出 64 張?zhí)卣鲌D;第二層卷積:卷積核尺寸 1×1 (f2×f2),卷積核數(shù)目 32 (n2),輸出 32 張?zhí)卣鲌D;第三層卷積:卷積核尺寸 5×5 (f3×f3),卷積核數(shù)目 1 (n3),輸出 1 張?zhí)卣鲌D即為最終重建高分辨率圖像。
訓(xùn)練
訓(xùn)練數(shù)據(jù)集
論文中某一實(shí)驗(yàn)采用 91 張自然圖像作為訓(xùn)練數(shù)據(jù)集,對(duì)訓(xùn)練集中的圖像先使用雙三次差值縮小到低分辨率尺寸,再將其放大到目標(biāo)放大尺寸,最后切割成諸多 33 × 33 圖像塊作為訓(xùn)練數(shù)據(jù),作為標(biāo)簽數(shù)據(jù)的則為圖像中心的 21 × 21 圖像塊(與卷積層細(xì)節(jié)設(shè)置相關(guān))。
損失函數(shù)
采用 MSE 函數(shù)作為卷積神經(jīng)網(wǎng)絡(luò)損失函數(shù)。
卷積層細(xì)節(jié)設(shè)置
第一層卷積核 9 × 9,得到特征圖尺寸為 (33-9)/1+1=25,第二層卷積核 1 × 1,得到特征圖尺寸不變,第三層卷積核 5 × 5,得到特征圖尺寸為 (25-5)/1+1=21。訓(xùn)練時(shí)得到的尺寸為 21 × 21,因此圖像中心的 21 × 21 圖像塊作為標(biāo)簽數(shù)據(jù)(卷積訓(xùn)練時(shí)不進(jìn)行 padding)。
!ls?/home/aistudio/work/
#?coding=utf-8
import?os
import?paddle.fluid?as?fluid
import?paddle.v2?as?paddle
from?PIL?import?Image
import?numpy?as?np
import?scipy.misc
import?scipy.ndimage
import?h5py
import?glob
FLAGS={"epoch":?10,"batch_size":?128,"image_size":?33,"label_size":?21,
??????"learning_rate":?1e-4,"c_dim":?1,"scale":?3,"stride":?14,
??????"checkpoint_dir":?"checkpoint","sample_dir":?"sample","is_train":?True}
#utils
def?read_data(path):
????with?h5py.File(path,?'r')?as?hf:
????????data?=?np.array(hf.get('data'))
????????label?=?np.array(hf.get('label'))
????????return?data,?label
def?preprocess(path,?scale=3):
????image?=?imread(path,?is_grayscale=True)
????label_?=?modcrop(image,?scale)
????label_?=?label_?/?255.
????input_?=?scipy.ndimage.interpolation.zoom(label_,?zoom=(1.?/?scale),?prefilter=False)??#?一次
????input_?=?scipy.ndimage.interpolation.zoom(input_,?zoom=(scale?/?1.),?prefilter=False)??#?二次,bicubic
????return?input_,?label_
def?prepare_data(dataset):
????if?FLAGS['is_train']:
????????data_dir?=?os.path.join(os.getcwd(),?dataset)
????????data?=?glob.glob(os.path.join(data_dir,?"*.bmp"))
????else:
????????data_dir?=?os.path.join(os.sep,?(os.path.join(os.getcwd(),?dataset)),?"Set5")
????????data?=?glob.glob(os.path.join(data_dir,?"*.bmp"))
????return?data
def?make_data(data,?label):
????if?not?os.path.exists('data/checkpoint'):
????????os.makedirs('data/checkpoint')
????if?FLAGS['is_train']:
????????savepath?=?os.path.join(os.getcwd(),?'data/checkpoint/train.h5')
????#?else:
????#?????savepath?=?os.path.join(os.getcwd(),?'data/checkpoint/test.h5')
????with?h5py.File(savepath,?'w')?as?hf:
????????hf.create_dataset('data',?data=data)
????????hf.create_dataset('label',?data=label)
def?imread(path,?is_grayscale=True):
????if?is_grayscale:
????????return?scipy.misc.imread(path,?flatten=True,?mode='YCbCr').astype(np.float)??#?將圖像轉(zhuǎn)灰度
????else:
????????return?scipy.misc.imread(path,?mode='YCbCr').astype(np.float)??#?默認(rèn)為false
def?modcrop(image,?scale=3):
????if?len(image.shape)?==?3:??#?彩色?800*600*3
????????h,?w,?_?=?image.shape
????????h?=?h?-?np.mod(h,?scale)
????????w?=?w?-?np.mod(w,?scale)
????????image?=?image[0:h,?0:w,?:]
????else:??#?灰度?800*600
????????h,?w?=?image.shape
????????h?=?h?-?np.mod(h,?scale)
????????w?=?w?-?np.mod(w,?scale)
????????image?=?image[0:h,?0:w]
????return?image
def?input_setup(config):
????if?config['is_train']:
????????data?=?prepare_data(dataset="data/data899/Train.zip_files/Train")
????else:
????????data?=?prepare_data(dataset="Test")
????sub_input_sequence?=?[]
????sub_label_sequence?=?[]
????padding?=?abs(config['image_size']?-?config['label_size'])?//?2??#?6?填充
????if?config['is_train']:
????????for?i?in?range(len(data)):
????????????input_,?label_?=?preprocess(data[i],?config['scale'])??#?data[i]為數(shù)據(jù)目錄
????????????if?len(input_.shape)?==?3:
????????????????h,?w,?_?=?input_.shape
????????????else:
????????????????h,?w?=?input_.shape
????????????for?x?in?range(0,?h?-?config['image_size']?+?1,?config['stride']):
????????????????for?y?in?range(0,?w?-?config['image_size']?+?1,?config['stride']):
????????????????????sub_input?=?input_[x:x?+?config['image_size'],?y:y?+?config['image_size']]??#?[33?x?33]
????????????????????sub_label?=?label_[x?+?padding:x?+?padding?+?config['label_size'],
????????????????????????????????y?+?padding:y?+?padding?+?config['label_size']]??#?[21?x?21]
????????????????????#?Make?channel?value,顏色通道1
????????????????????sub_input?=?sub_input.reshape([config['image_size'],?config['image_size'],?1])
????????????????????sub_label?=?sub_label.reshape([config['label_size'],?config['label_size'],?1])
????????????????????sub_input_sequence.append(sub_input)
????????????????????sub_label_sequence.append(sub_label)
????????arrdata?=?np.asarray(sub_input_sequence)??#?[?,?33,?33,?1]
????????arrlabel?=?np.asarray(sub_label_sequence)??#?[?,?21,?21,?1]
????????make_data(arrdata,?arrlabel)??#?把處理好的數(shù)據(jù)進(jìn)行存儲(chǔ),路徑為checkpoint/..
????else:
????????input_,?label_?=?preprocess(data[4],?config['scale'])
????????if?len(input_.shape)?==?3:
????????????h,?w,?_?=?input_.shape
????????else:
????????????h,?w?=?input_.shape
????????input?=?input_.reshape([h,?w,?1])
????????label?=?label_[6:h?-?6,?6:w?-?6]
????????label?=?label.reshape([h?-?12,?w?-?12,?1])
????????sub_input_sequence.append(input)
????????sub_label_sequence.append(label)
????????input1?=?np.asarray(sub_input_sequence)
????????label1?=?np.asarray(sub_label_sequence)
????????return?input1,?label1,?h,?w
def?imsave(image,?path):
????return?scipy.misc.imsave(path,?image)
#train
def?reader_creator_image_and_label():
????input_setup(FLAGS)
????data_dir=?os.path.join('./data/{}'.format(FLAGS['checkpoint_dir']),?"train.h5")
????images,labels=read_data(data_dir)
????def?reader():
????????for?i?in?range(len(images)):
????????????yield?images,?labels
????return?reader
def?train(use_cuda,?num_passes,BATCH_SIZE?=?128,?model_save_dir='../models'):
????if?FLAGS['is_train']:
??????images?=?fluid.layers.data(name='images',?shape=[1,?FLAGS['image_size'],?FLAGS['image_size']],?dtype='float32')
??????labels?=?fluid.layers.data(name='labels',?shape=[1,?FLAGS['label_size'],?FLAGS['label_size']],?dtype='float32')
????else:
??????_,_,FLAGS['image_size'],FLAGS['label_size']=input_setup(FLAGS)
??????images?=?fluid.layers.data(name='images',?shape=[1,?FLAGS['image_size'],?FLAGS['label_size']],?dtype='float32')
??????labels?=?fluid.layers.data(name='labels',?shape=[1,?FLAGS['image_size']-12,?FLAGS['label_size']-12],?dtype='float32')
????#feed_order=['images','labels']
????#?獲取神經(jīng)網(wǎng)絡(luò)的訓(xùn)練結(jié)果
????predict?=?model(images)
????#?獲取損失函數(shù)
????cost?=?fluid.layers.square_error_cost(input=predict,?label=labels)
????#?定義平均損失函數(shù)
????avg_cost?=?fluid.layers.mean(cost)
????#?定義優(yōu)化方法
????optimizer?=?fluid.optimizer.Momentum(learning_rate=1e-4,momentum=0.9)
????opts?=optimizer.minimize(avg_cost)
????#?是否使用GPU
????place?=?fluid.CUDAPlace(0)?if?use_cuda?else?fluid.CPUPlace()
????#?初始化執(zhí)行器
????exe=fluid.Executor(place)
????exe.run(fluid.default_startup_program())
????#?獲取訓(xùn)練數(shù)據(jù)
????train_reader?=?paddle.batch(
????????reader_creator_image_and_label(),?batch_size=BATCH_SIZE)
????#?獲取測(cè)試數(shù)據(jù)
????#?test_reader?=?paddle.batch(
????#?????read_data(),?batch_size=BATCH_SIZE)
????#print(len(next(train_reader())))
????feeder?=?fluid.DataFeeder(place=place,?feed_list=[images,?labels])
????for?pass_id?in?range(num_passes):
????????for?batch_id,?data?in?enumerate(train_reader()):
????????????avg_cost_value?=?exe.run(fluid.default_main_program(),
????????????????????????????????????feed=feeder.feed(data),
????????????????????????????????????fetch_list=[avg_cost])
????????????if?batch_id%100?==?0:
????????????????print("loss="+avg_cost_value[0])
def?model(images):
????conv1=fluid.layers.conv2d(input=images,?num_filters=64,?filter_size=9,?act='relu')
????conv2=fluid.layers.conv2d(input=conv1,?num_filters=32,?filter_size=1,act='relu')
????conv3=fluid.layers.conv2d(input=conv2,?num_filters=1,?filter_size=5)
????return?conv3
if?__name__?==?'__main__':
????#?開(kāi)始訓(xùn)練
????train(use_cuda=False,?num_passes=10)
測(cè)試
全卷積網(wǎng)絡(luò)
所用網(wǎng)絡(luò)為全卷積網(wǎng)絡(luò),因此作為實(shí)際測(cè)試時(shí),直接輸入完整圖像即可。
Padding
訓(xùn)練時(shí)得到的實(shí)際上是除去四周 (33-21)/2=6 像素外的圖像,若直接采用訓(xùn)練時(shí)的設(shè)置(無(wú) padding),得到的圖像最后會(huì)減少四周各 6 像素(如插值放大后輸入 512 × 512,輸出 500 × 500)。
因此在測(cè)試時(shí)每一層卷積都進(jìn)行了 padding(卷積核尺寸為 1 × 1的不需要進(jìn) 行 padding),這樣保證插值放大后輸入與輸出尺寸的一致性。
重建結(jié)果
客觀評(píng)價(jià)指標(biāo) PSNR 與 SSIM:相比其他傳統(tǒng)方法,SRCNN 取得更好的重建效果。
主觀效果:相比其他傳統(tǒng)方法,SRCNN 重建效果更具優(yōu)勢(shì)。
關(guān)于PaddlePaddle
PaddlePaddle 在我看來(lái)與 TensorFlow 有點(diǎn)類似,API 都非常好用,用戶群體越來(lái)越大。不過(guò)有一點(diǎn)小建議,就是在我使用的時(shí)候,發(fā)現(xiàn)數(shù)據(jù)處理的方式比較麻煩(比較菜),比如 TensorFlow中有 TFRecords,同時(shí)有自帶的數(shù)據(jù)集迭代器可以很方便的導(dǎo)入數(shù)據(jù),所以希望出一個(gè)官方的數(shù)據(jù)轉(zhuǎn)換格式,用來(lái)讀取數(shù)據(jù),并提供迭代器方法。
點(diǎn)擊標(biāo)題查看更多論文復(fù)現(xiàn):?
經(jīng)典論文復(fù)現(xiàn) | LSGAN:最小二乘生成對(duì)抗網(wǎng)絡(luò)
PyraNet:基于特征金字塔網(wǎng)絡(luò)的人體姿態(tài)估計(jì)
經(jīng)典論文復(fù)現(xiàn) | InfoGAN:一種無(wú)監(jiān)督生成方法
經(jīng)典論文復(fù)現(xiàn) | 基于標(biāo)注策略的實(shí)體和關(guān)系聯(lián)合抽取
#投 稿 通 道#
?讓你的論文被更多人看到?
如何才能讓更多的優(yōu)質(zhì)內(nèi)容以更短路徑到達(dá)讀者群體,縮短讀者尋找優(yōu)質(zhì)內(nèi)容的成本呢??答案就是:你不認(rèn)識(shí)的人。
總有一些你不認(rèn)識(shí)的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學(xué)者和學(xué)術(shù)靈感相互碰撞,迸發(fā)出更多的可能性。?
PaperWeekly 鼓勵(lì)高校實(shí)驗(yàn)室或個(gè)人,在我們的平臺(tái)上分享各類優(yōu)質(zhì)內(nèi)容,可以是最新論文解讀,也可以是學(xué)習(xí)心得或技術(shù)干貨。我們的目的只有一個(gè),讓知識(shí)真正流動(dòng)起來(lái)。
??來(lái)稿標(biāo)準(zhǔn):
? 稿件確系個(gè)人原創(chuàng)作品,來(lái)稿需注明作者個(gè)人信息(姓名+學(xué)校/工作單位+學(xué)歷/職位+研究方向)?
? 如果文章并非首發(fā),請(qǐng)?jiān)谕陡鍟r(shí)提醒并附上所有已發(fā)布鏈接?
? PaperWeekly 默認(rèn)每篇文章都是首發(fā),均會(huì)添加“原創(chuàng)”標(biāo)志
? 投稿郵箱:
? 投稿郵箱:hr@paperweekly.site?
? 所有文章配圖,請(qǐng)單獨(dú)在附件中發(fā)送?
? 請(qǐng)留下即時(shí)聯(lián)系方式(微信或手機(jī)),以便我們?cè)诰庉嫲l(fā)布時(shí)和作者溝通
?
現(xiàn)在,在「知乎」也能找到我們了
進(jìn)入知乎首頁(yè)搜索「PaperWeekly」
點(diǎn)擊「關(guān)注」訂閱我們的專欄吧
關(guān)于PaperWeekly
PaperWeekly 是一個(gè)推薦、解讀、討論、報(bào)道人工智能前沿論文成果的學(xué)術(shù)平臺(tái)。如果你研究或從事 AI 領(lǐng)域,歡迎在公眾號(hào)后臺(tái)點(diǎn)擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。
▽ 點(diǎn)擊 |?閱讀原文?| 收藏復(fù)現(xiàn)代碼
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的经典论文复现 | 基于深度卷积网络的图像超分辨率算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: NeurIPS 2018 | 基于自监督
- 下一篇: 最小熵原理:“物以类聚”之从图书馆到词向