20210504のTensorFlowに関する記事は2件です。

Google Colab で超解像(EDSR)

はじめに 深層学習を使用した単一画像超解像について記事を書くが、すでに多くの記事があるので、個人的に気づきがあった点を重点的に記すことにする。一通りの処理を実装したGoogle Colabのノートブックも公開する。 単一画像超解像とは Single Image Super Resolution(SISR)は低画質画像を入力として、高画質画像を出力とする処理。 2018年ぐらいまでの主要な手法については下記の記事に詳しい。 トップ学会採択論文にみる、超解像ディープラーニング技術のまとめ 下記論文には各種手法の性能比較等がある。 A Deep Journey into Super-resolution: A Survey 以下のページは最新を含む多くの関連論文へのリンクがある。 Awesome-Super-Resolution この記事では、実装しやすい割りに高性能なEDSRを実装する。 Enhanced Deep Residual Networks for Single Image Super-Resolution Code 実装上の注意点 以下、実装して気づいた点など Datasetの生成 これはTensorflow(というかTPU)固有の問題かもしれないが、意外と工数がかかったので最初に記す。 SISRでは標準的にDIV2Kと言うデータが学習用に使用される。本来はここからランダムに領域を抽出して学習データにするのだが、高画質画像のPNGをまとめたzipファイルでも3GBあり、学習データはオンメモリにするのではなくファイルから逐次読み出したいのだが、TPUではファイルアクセスさせるようなDatasetはエラーが出る(Google Storage Serviceが必要)。 仕方がないので事前に一部の領域をパッチとして用意して、tf.data.Dataset.from_tensor_slicesを使ってDatasetを生成する事にした。from_tensor_slices自体にもデータ量の制限(多分2G)があるようで、それを超えるサイズを作りたい場合は個別に生成してからconcatenateで結合させる必要があった。 今回の実装は、パッチの数を減らすために事前の前処理でエッジが多い領域を優先して切り抜くことで、約半分の領域だけ使って学習させることにした。 PSNRの計算方法 超解像によって画像をどの程度復元できたか、はPSNR(Peak Signal-to-Noise Ratio)などを使って定量的に評価する。超解像ではPSNRはRGBで計算するのではなく、以下のような方法で行うのが一般的なようだ。 YCbCRのY成分のみで、Yの範囲は16〜235 画像端の数ピクセルは計算対象外とする 正則化手法は使わない CNNを使った画像認識では各種正則化で性能向上させることが一般的だが、超解像ではCNNを使う場合でも正則化は効果が無いか寧ろ逆効果な場合が多いようだ。 したがってEDSRではBatchNormalizationやWeightDecay等は使わない。 また、画像認識ではOptimizerにSGDを使った方がAdam等より性能が良い場合がほとんどだが、超解像ではAdamがよく使われ、実際にEDSRではAdamの方が好成績だった。SGD自体に正則化の効果があるようなので関連があるのではないかと思われる。 「画像認識は抽象化の学習」で「超解像では具体化の学習」だから、と言う説明でそれなりに納得はするが、なんだか不思議な感じがする。 実装 事前のパッチ化 先に述べたDatasetの作成処理で、大体5分ぐらいかかるが、この時間はTPUを使うことの高速化で挽回できる。時間がかかるので進捗表示にtqdmを始めて使ってみたが、これは便利。 事前にDIV2KのZipファイルをダウンロードしておくことが必要。 import zipfile import os import io import random from PIL import Image from PIL import ImageFilter import numpy as np from tqdm.notebook import tqdm from tensorflow.keras import backend import tensorflow as tf def make_dataset_from_div2k( filenames, num_patches, scale=2, patch_size = 96): print("make_dataset_from_zip", filenames) lr_patch_size = patch_size//scale hr_filename = filenames[0] lr_filename = filenames[1] with zipfile.ZipFile(hr_filename) as hr_zip: img_files = 0 for img_filename in hr_zip.namelist(): if not img_filename.endswith(".png"): continue img_files += 1 pbar = tqdm(total=img_files) img_count = 0 average_patches_in_file = num_patches/img_files imgbuf = None patches_list = [] dataset = None file_count = 0 for img_filename in hr_zip.namelist(): if not img_filename.endswith(".png"): continue lr_img_filename = f'{img_filename[0:11]}_LR_bicubic/X{scale}/{img_filename[-8:-4]}x{scale}.png' with hr_zip.open(img_filename) as img_file: img_bin = io.BytesIO(img_file.read()) img = Image.open(img_bin) num_h = img.height//patch_size num_w = img.width//patch_size patches = num_h*num_w patches_list.append(patches) patch_list = [] for h in range(num_h): for w in range(num_w): x = w * patch_size y = h * patch_size cropped = img.crop((x, y, x+patch_size, y+patch_size)) img_find_edges = cropped.filter(ImageFilter.FIND_EDGES) patch_list.append( {"score":np.mean(img_find_edges), "img":cropped, "pos":(x,y) }) patch_list.sort(reverse=True,key=lambda x:x['score']) patches_in_file = 0 with zipfile.ZipFile(lr_filename) as lr_zip: with lr_zip.open(lr_img_filename) as img_file: img_bin = io.BytesIO(img_file.read()) img = Image.open(img_bin) patches_in_file = int(average_patches_in_file*(file_count+1)-img_count) patches_in_file = min(patches, patches_in_file) for i in range(int(patches_in_file)): if imgbuf is None: remain = num_patches-img_count if remain==0: break img_count_in_ds = 0 num_patches_in_ds = min(1024*32, remain) imgbuf=np.zeros((num_patches_in_ds, patch_size, patch_size, 3), dtype=np.uint8) lr_imgbuf=np.zeros((num_patches_in_ds, lr_patch_size, lr_patch_size, 3), dtype=np.uint8) imgbuf[img_count_in_ds] = np.array( patch_list[i]["img"] ) x,y = patch_list[i]["pos"] lr_img = img.crop((x//scale, y//scale, (x+patch_size)//scale, (y+patch_size)//scale)) lr_imgbuf[img_count_in_ds] = np.array( lr_img ) img_count+=1 img_count_in_ds+=1 if img_count_in_ds==num_patches_in_ds: if dataset is None: dataset = tf.data.Dataset.from_tensor_slices((lr_imgbuf,imgbuf)) else: dataset = dataset.concatenate( tf.data.Dataset.from_tensor_slices((lr_imgbuf,imgbuf)) ) imgbuf = None lr_imgbuf = None img.close() file_count+=1 # print(file_count) pbar.update(1) if imgbuf is not None: print("!!") # print(min(patches_list)) pbar.close() return dataset EDSR 以下、EDSRのコード。大変シンプルだが、scaling_factorで調整しないとモデルが崩壊する場合がある。この辺が正則化を入れない弊害か。 モデルとしてはInputでサイズを指定していないので、メモリが許す限りどんなサイズの画像でも入力可能。 論文では48x48を低画質のパッチとし、高画質画像は倍率(2/3/4倍)のサイズをかけた大きさの画像として訓練しているが、この実装では96x96を高画質のパッチとして、2/3/4で割ったサイズの画像を低画質の入力サイズとしているので、2倍以外は論文の学習よりも計算量が減っている。 import tensorflow as tf import tensorflow.keras.layers as layers import numpy as np DIV2K_RGB_MEAN = np.array([0.4488, 0.4371, 0.4040])* 255 def normalize(x, rgb_mean=DIV2K_RGB_MEAN): return (x - rgb_mean) / 127.5 def denormalize(x, rgb_mean=DIV2K_RGB_MEAN): return (x * 127.5) + rgb_mean def upsample(x, scale, num_filters): if 2 <= scale <= 3 : x = layers.Conv2D(num_filters * (scale ** 2), 3, padding='same')(x) x = layers.Lambda(lambda y: tf.nn.depth_to_space(y, scale))(x) elif scale == 4: x = layers.Conv2D(num_filters * (2 ** 2), 3, padding='same')(x) x = layers.Lambda(lambda y: tf.nn.depth_to_space(y, 2))(x) x = layers.Conv2D(num_filters * (2 ** 2), 3, padding='same')(x) x = layers.Lambda(lambda y: tf.nn.depth_to_space(y, 2))(x) return x def create_model(num_filters=64, num_res_blocks=8, scale=2, scaling_factor=None, activation='relu'): inputs = layers.Input( (None, None, 3) ) CONV_KWARGS ={ 'padding':'same', 'kernel_initializer':'he_uniform', 'bias_initializer':'he_uniform', } lowres = layers.Lambda(normalize)(inputs) # EDSR def res_block(x_in, filters, scaling=None): x = layers.Conv2D(filters, 3, activation=activation, **CONV_KWARGS)(x_in) x = layers.Conv2D(filters, 3, **CONV_KWARGS)(x) if scaling: x = layers.Lambda(lambda t: t * scaling)(x) x = layers.Add()([x_in, x]) return x x = lowres = layers.Conv2D(num_filters, 3, **CONV_KWARGS)(lowres) for i in range(num_res_blocks): x = res_block(x, num_filters, scaling=scaling_factor) x = layers.Conv2D(num_filters, 3, **CONV_KWARGS)(x) lowres = layers.Add()([x, lowres]) # Upsampling highres = upsample(lowres, scale, num_filters) highres = layers.Conv2D(3, 3, **CONV_KWARGS)(highres) highres = layers.Lambda(denormalize)(highres) return tf.keras.models.Model(inputs, highres) 実験結果 Google Colabのノートブック 論文に近い形で実装したつもりだが、学習率に関しては少し調整してある。 Set5/Set14/BSD100/Urban100は超解像の評価によく用いられるデータセットで下記のgithubからダンロードしている。 https://github.com/jbhuang0604/SelfExSR.git DIV2KについてはGoogle Drive内にzipがある前提の実装なので、実際に試す場合は最初のセルを適宜修正する必要がある。 訓練時間短縮のため、DIV2Kは訓練データのみ使用し、5エポックに1回Set14のPSNRを計測するように実装してある。 以下4xでの実験結果で、数値はPSNR。最後の行以外は論文内の値。 Method Set5 Set14 BSD100 Urban100 Bicubic 28.42 26.00 25.96 23.14 EDSR(Paper) 32.46 28.80 27.71 26.64 EDSR 32.40 28.72 27.56 26.29 実験では、論文の値に若干届かないものの、かなり近いところまで再現できている。 EDSR(パラメータ数40M)だと3時間程度かかるが、論文にあるbaseline設定(パラメータ数1.5M)なら30分ぐらいで終わる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tensorflow2.4 Install to Ubuntu20.04 備忘録

概要 TF2.4を入れていたUbuntu18.05のAnacondaの具合が悪い。 fit()中のエラーが解消できなくなり、OSをUbunto20.04にして再セットアップした。 実施時期: 2021年5月 OS: Ubuntu20.04LTS (Linux ryzen 5.8.0-50-generic #56~20.04.1-Ubuntu) CPU: Ryzen 5600X GPU: GeForce RTX3070 Python: 標準のPython3 経緯 Anacondaの問題だったので今回はAnacondaは使わない。 前回UpしたとおりcondaでTF2.4.1が入るようになったけど、同時に入るCUDAが依然として10.1のままだったり、怪しい部分があるから。 RTX 30X0 + TF2.4.1 on Ubuntu20.04について、Stack Overflowにスレを立てたけど大した情報が集まらなかった。 https://stackoverflow.com/questions/67364950/cuda-and-cudnn-version-conflict-against-tensorflow2-4-1 前準備 CUDA対応NVIDIAボードが刺さっていることを確認する。 $ lspci | grep -i nvidia 07:00.0 VGA compatible controller: NVIDIA Corporation Device 2484 (rev a1) 07:00.1 Audio device: NVIDIA Corporation Device 228b (rev a1) Nvidia Driverのインストール CUDAをインストールするとき自動的にドライバは一緒に入るが、バーションがr450になるかもしれず、RTX 3070はr457以降が対応しているので、あえて明示的に最新版のr460を入れる。 $ sudo ubuntu-drivers devices == /sys/devices/pci0000:00/0000:00:03.1/0000:07:00.0 == modalias : pci:v000010DEd00002484sv00001462sd00003909bc03sc00i00 vendor : NVIDIA Corporation driver : nvidia-driver-460 - distro non-free recommended driver : xserver-xorg-video-nouveau - distro free builtin $ sudo apt install --no-install-recommends nvidia-driver-460 cuda, cuDNNのサポート表を確認 TFオフィシャルの手順はUbuntu16, 18のみでUbuntu 20向けには書かれていない。 Ubuntu, NVIDIA driver, cuda, cuDNNの各バージョンは厳密に合わせておく必要があるので下表で確認する。 TF2.4.1の要求から本PCの構成で選択したのは下記の組み合わせ Driver: r460 cuda: CUDA Toolkit 11.0 Update 1 Downloads cuDNN: cudnn-11.0-linux-x64-v8.0.4.30.tgz cudaのインストール 下記のrunfile[local]を行う。deb[local]でうまく行かなかったため。 $ wget https://developer.download.nvidia.com/compute/cuda/11.0.3/local_installers/cuda_11.0.3_450.51.06_linux.run $ sudo sh cuda_11.0.3_450.51.06_linux.run # ① $ sudo sh ./cuda_11.0.3_450.51.06_linux.run --toolkit --silent –override # ② ①を実行したときに、警告‘Existing package manager installation of the driver found.’ が表示され、継続せずに手動で削除して再度実行するように言われた。どのDriverのことなのか書かれていなかったが多分先に入れたr460のことだと思う。 r460はそのままにしたいので、①は再実行せず下記をおこなった。 $ sudo sh ./cuda_11.0.3_450.51.06_linux.run --toolkit --silent –override # ② 実行後、/usr/local/cuda-11.0/bin フォルダができていることを確認してbashrcに以下を追加する。 export PATH=/usr/local/cuda-11.0/bin${PATH:+:${PATH}} export LD_LIBRARY_PATH=/usr/local/cuda-11.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRAR cuDNNのインストール 予め下記の cudnn-11.0-linux-x64-v8.0.4.30.tgz ファイルをダウントードしておく。 Download cuDNN v8.0.4 (September 28th, 2020), for CUDA 11.0  cuDNN Library for Linux [x86_64] cd Downloads tar -xzvf cudnn-11.0-linux-x64-v8.0.4.30.tgz sudo cp cuda/include/cudnn*.h /usr/local/cuda/include sudo cp -P cuda/lib64/libcudnn* /usr/local/cuda/lib64 sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn* sudo apt update Tensorflow2.4.1のインストール まず、仮想環境などを作成するためTFオフィシャルのとおりに実行する。 なお、Documents/ML/tf-test1に仮想環境test1を作成しそこにTFを入れる。 sudo apt install python3-dev python3-pip python3-venv cd Documents/ML/tf-test1 python3 -m venv test1 source test1/bin/activate pip install --upgrade pip pip list pip install --upgrade tensorflow pip install pandas pip install matplotlib pip install seaborn pip install plotly pip install jupyterlab pip install scipy pip install scikit-learn pip install gpyopt pip install trax==1.3.7 Pythonを起動し下記でTFがGPUを認識していることを確認する。 import tensorflow as tf print(tf.__version__) print(tf.config.list_physical_devices('GPU')) print(tf.test.gpu_device_name())
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む