20210501のTensorFlowに関する記事は5件です。

tensorflow2でCRFを使った学習

はじめに tensorflow1では、keras-contribのcrf_lossとcrf_accuracyを使うことで、簡単に学習が可能でした。 tensorflow2以降、CRFを使った学習が分かりづらくなりました。 日本語での解説記事はほとんど見つかりませんでした。 tensorflow2では、lossとaccuracyを定義したモデルを自分で作る必要があるようです。 環境 tensorflow 2.4.1 tensorflow-addons 0.12.1 モデル model.py import tensorflow as tf from tensorflow.keras import layers as L from tensorflow_addons.layers.crf import CRF from tensorflow.python.keras.engine import data_adapter from tensorflow_addons.text.crf import crf_log_likelihood class ModelWithCRFLoss(tf.keras.Model): def __init__(self, output_names, *args, **kwargs): self.__output_names = output_names super().__init__( *args, **kwargs) self._accuracy_func = [tf.keras.metrics.Accuracy(name=name + "_accuracy") for name in self.__output_names] def crf_loss(self, y_true, y_pred): viterbi_sequence, potentials, sequence_length, chain_kernel = y_pred loss = -crf_log_likelihood(potentials, y_true, sequence_length, chain_kernel)[0] / tf.cast(sequence_length, tf.float32) return viterbi_sequence, sequence_length, tf.reduce_mean(loss) def compute_loss(self, x, y, sample_weight, training=False): y_preds = self(x, training=training) total_loss = 0.0 results = [] for y_true, pred in zip(y, y_preds): viterbi_sequence, sequence_length, loss = self.crf_loss(y_true, pred) total_loss += loss results.append([viterbi_sequence, sequence_length, loss]) return results, total_loss def train_step(self, data): data = data_adapter.expand_1d(data) x, y, sample_weight = data_adapter.unpack_x_y_sample_weight(data) with tf.GradientTape() as tape: results, total_loss = self.compute_loss(x, y, sample_weight, training=True) gradients = tape.gradient(total_loss, self.trainable_variables) self.optimizer.apply_gradients(zip(gradients, self.trainable_variables)) m = {"loss": total_loss} for i in range(len(results)): m[self.__output_names[i] + "_loss"] = results[i][-1] for i in range(len(results)): self._accuracy_func[i].update_state(y[i], results[i][0]) m[self._accuracy_func[i].name] = self._accuracy_func[i].result() return m def test_step(self, data): data = data_adapter.expand_1d(data) x, y, sample_weight = data_adapter.unpack_x_y_sample_weight(data) with tf.GradientTape() as tape: results, total_loss = self.compute_loss(x, y, sample_weight, training=False) m = {"loss": total_loss} for i in range(len(results)): m[self.__output_names[i] + "_loss"] = results[i][-1] for i in range(len(results)): self._accuracy_func[i].update_state(y[i], results[i][0]) m[self._accuracy_func[i].name] = self._accuracy_func[i].result() return m def create_model(): input_layer = L.Input(shape=(None, 256)) x = L.Dense(256)(input_layer) x = L.LSTM(256, return_sequences=True)(x) x = L.Dense(256)(x) x = CRF(13, name="out")(x) # output_namesには、出力層の名前を指定します。 # 出力が複数のレイヤーの場合は、順序を間違えないようにしてください。 model = ModelWithCRFLoss(inputs=[input_layer], outputs=[x], output_names=["out"]) return model Model: "model_with_crf_loss" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, None, 256)] 0 _________________________________________________________________ dense (Dense) (None, None, 256) 65792 _________________________________________________________________ lstm (LSTM) (None, None, 256) 525312 _________________________________________________________________ dense_1 (Dense) (None, None, 256) 65792 _________________________________________________________________ out (CRF) [(None, None), (None, Non 3536 ================================================================= Total params: 660,434 Trainable params: 660,432 Non-trainable params: 2 _________________________________________________________________ 使い方は、tf.keras.ModelをModelWithCRFLossに置き換えるだけです。 出力は2次元(batch、time_steps)になることに注意してください。 one-hotベクトルの (batch, time_steps, classes) は利用できません。 訓練 訓練は、通常のモデルと同じように行えます。 lossとaccuracyはモデルで定義されているので、compileではオプティマイザを指定するだけです。 train.py from model import create_model from tensorflow_addons.optimizers import RAdam model = create_model() optimizer = RAdam() # optimizerだけを指定 model.compile(optimizer) おわりに tensorflow2でCRFを使ったトレーニングができるようになりました。 ですが、keras-contribのCRFに比べて倍近く速度が遅いです。 早く改善されることを期待してます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepLearningを用いた超解像手法/VESPCNの実装

概要 深層学習を用いた、動画像における超解像手法であるVESPCNの実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するVESPCNは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像手法は、3枚の連続するフレームを入力して、1枚の高解像度画像を出力します。 前半のMotion estimationが動き補償などのデータ前処理の部分、 後半のSpatio-temporal ESPCNが超解像を行う部分になっています。 それぞれの流れについてもう少し詳しく説明していきます。 ① Motion estimation 超解像アルゴリズムに入力する前のデータ前処理を行う箇所になります。 Motion estimation部分のアルゴリズムは以下の図の通りです。(図は論文から引用) このデータ前処理の部分では、2枚の連続したフレームを入力します。 ここでは、Coarse flow estimationとFine flow estimationという2つのニューラルネットワークを通してデータ前処理を行います。 $\Delta c$と$\Delta f$は移動量を表すパラメータみたいなもので、オプティカルフローのパラメータを求めるのと似ています。 今回は、このデータ前処理でもニューラルネットワークを使用しています。 過去の超解像手法では、オプティカルフローを事前に求めたり、Bicubic法などで補間処理を行っていたため、データ前処理にかなり時間がかかってました。 そのため、データ前処理にもニューラルネットワークを使用することで、計算速度を上げる狙いもあります。 ニューラルネットワークのパラメータは、以下の通りです。(図は論文から引用) kはフィルターのサイズ、nはフィルターの数、sはストライドの大きさを表しています。 それぞれ6つの層から構成されており、最後にupscaleで拡大しています。 この拡大の方法は、ESPCNで提案されたPixel shuffleを使用します。(図は論文から引用) 特徴としては、拡大したい倍率の2乗の値を入力する点です。 論文の例だと、5層でチャンネル数を32に拡張しています。 4倍拡大なので、4の2乗の16と2チャンネルというように分けることができ、4倍拡大された2チャンネルの結果を返します。 ② Spatio-temporal ESPCN Spatio-temporal ESPCNは、ESPCNを用いて、実際に超解像を行います。 ESPCNは、単一画像にのみ対応しているものでしたが、今回の論文では動画像に対応できるように入力チャンネル数を変更したりしています。 ESPCNの構造は前でも載せましたが、このようになっています。(図は論文から引用) ESPCNについては、以前に実装をしているので興味があればそちらもご覧ください。(実装記事) ESPCNで処理を行えば、結果が出力され本モデルの全ての工程が終わります。 3. 実装したアルゴリズム 今回は、VESPCNの全体を1つのニューラルネットワークのモデルとして実装してみました。 下の図が、本モデルの概要に近いと思います。(図は論文から引用) 入力する低解像度画像は3枚で、まずMotion estimationでデータ前処理を行います。 その後、ESPCNに3枚の画像を入力するという流れです。 コマンドラインでモデルを出力した例は、長すぎるので最後に参考として載せています。 4. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 5. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 6. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 160 #HRのサイズ train_width = 160 test_height = 720 test_width = 1280 train_dataset_num = 20000 #生成する学習用データの数 test_dataset_num = 10 #生成するテスト用データの数 train_cut_num = 10 #一組のフレームから生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画像のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 3 #入力するLRの数 input_channels = 1 #入力するLRのチャンネル数 mag = 4 #拡大倍率 MAX_BATSH_SIZE = 128 #最大のバッチサイズ EPOCHS = 1000 後は、学習のパラメータをあれこれ好きな値に設定します。85~107行目です。 今回は、バッチサイズの開始が1で、10epochごとに2倍にしていく、という構造だったため、少しコードを変更しています。 具体的にはMAX_BATCH_SIZEを2で割り続けて、2を何乗すればいいか計算し、その回数に応じて10回で学習させています。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) x = MAX_BATSH_SIZE i = 0 while x > 1: x /= 2 i += 1 for n in range(i): train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = 10, verbose = 2, batch_size = 2 ** n) train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = EPOCHS - 10 * i, verbose = 2, batch_size = MAX_BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は3枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 7. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:27.54 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) だいぶ粗さが取れているのが目視でもわかります。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみると、高解像度化がされているのが分かります。 実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得するとOKです。 8. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 9. まとめ 今回は、最近読んだ論文のVESPCNを元に実装してみました。 モデルが複雑すぎていつもより実装に時間がかかりましたが、無事に実装できました。 次は単一画像の超解像手法の実装をしてみようと思います。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 参考文献 ・Real-Time Video Super-Resolution with Spatio-Temporal Networks and Motion Compensation  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。 [参考]本モデルのコマンドライン出力 参考として、上記で軽く触れた本モデルのコマンドラインを紹介します。 (各フレームのMotion estimationなども全て1つのモデルにまとめているので入り乱れています。) 結構長めのモデルになってしまいました。 __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_t_minus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t_plus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_minus_1[0][0] __________________________________________________________________________________________________ concatenate_2 (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_plus_1[0][0] __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 2 1224 concatenate[0][0] __________________________________________________________________________________________________ conv2d_10 (Conv2D) (None, None, None, 2 1224 concatenate_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 2 5208 conv2d[0][0] __________________________________________________________________________________________________ conv2d_11 (Conv2D) (None, None, None, 2 5208 conv2d_10[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 2 14424 conv2d_1[0][0] __________________________________________________________________________________________________ conv2d_12 (Conv2D) (None, None, None, 2 14424 conv2d_11[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 2 5208 conv2d_2[0][0] __________________________________________________________________________________________________ conv2d_13 (Conv2D) (None, None, None, 2 5208 conv2d_12[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 6944 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_14 (Conv2D) (None, None, None, 3 6944 conv2d_13[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 2 0 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 2 0 conv2d_14[0][0] __________________________________________________________________________________________________ multiply (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] lambda[0][0] __________________________________________________________________________________________________ multiply_2 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] lambda_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem (Slici (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_1 (Sli (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_4 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_5 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.expand_dims (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem[0][0] __________________________________________________________________________________________________ tf.expand_dims_1 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_1[0][0] __________________________________________________________________________________________________ tf.expand_dims_4 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_4[0][0] __________________________________________________________________________________________________ tf.expand_dims_5 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_5[0][0] __________________________________________________________________________________________________ add (Add) (None, None, None, 1 0 tf.expand_dims[0][0] tf.expand_dims_1[0][0] __________________________________________________________________________________________________ add_5 (Add) (None, None, None, 1 0 tf.expand_dims_4[0][0] tf.expand_dims_5[0][0] __________________________________________________________________________________________________ add_1 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add[0][0] __________________________________________________________________________________________________ add_6 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_minus_1[0][0] lambda[0][0] add_1[0][0] __________________________________________________________________________________________________ concatenate_3 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_plus_1[0][0] lambda_2[0][0] add_6[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 2 3024 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_15 (Conv2D) (None, None, None, 2 3024 concatenate_3[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 2 5208 conv2d_5[0][0] __________________________________________________________________________________________________ conv2d_16 (Conv2D) (None, None, None, 2 5208 conv2d_15[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 2 5208 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_17 (Conv2D) (None, None, None, 2 5208 conv2d_16[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 2 5208 conv2d_7[0][0] __________________________________________________________________________________________________ conv2d_18 (Conv2D) (None, None, None, 2 5208 conv2d_17[0][0] __________________________________________________________________________________________________ conv2d_9 (Conv2D) (None, None, None, 8 1736 conv2d_8[0][0] __________________________________________________________________________________________________ conv2d_19 (Conv2D) (None, None, None, 8 1736 conv2d_18[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 2 0 conv2d_9[0][0] __________________________________________________________________________________________________ lambda_3 (Lambda) (None, None, None, 2 0 conv2d_19[0][0] __________________________________________________________________________________________________ add_2 (Add) (None, None, None, 2 0 lambda[0][0] lambda_1[0][0] __________________________________________________________________________________________________ add_7 (Add) (None, None, None, 2 0 lambda_2[0][0] lambda_3[0][0] __________________________________________________________________________________________________ multiply_1 (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] add_2[0][0] __________________________________________________________________________________________________ multiply_3 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] add_7[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_2 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_3 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_6 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_7 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_2 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_2[0][0] __________________________________________________________________________________________________ tf.expand_dims_3 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_6 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_6[0][0] __________________________________________________________________________________________________ tf.expand_dims_7 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_7[0][0] __________________________________________________________________________________________________ add_3 (Add) (None, None, None, 1 0 tf.expand_dims_2[0][0] tf.expand_dims_3[0][0] __________________________________________________________________________________________________ add_8 (Add) (None, None, None, 1 0 tf.expand_dims_6[0][0] tf.expand_dims_7[0][0] __________________________________________________________________________________________________ add_4 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add_3[0][0] __________________________________________________________________________________________________ add_9 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_8[0][0] __________________________________________________________________________________________________ concatenate_4 (Concatenate) (None, None, None, 3 0 add_4[0][0] input_t[0][0] add_9[0][0] __________________________________________________________________________________________________ conv2d_20 (Conv2D) (None, None, None, 9 684 concatenate_4[0][0] __________________________________________________________________________________________________ conv2d_21 (Conv2D) (None, None, None, 3 2624 conv2d_20[0][0] __________________________________________________________________________________________________ conv2d_22 (Conv2D) (None, None, None, 1 4624 conv2d_21[0][0] __________________________________________________________________________________________________ lambda_4 (Lambda) (None, None, None, 1 0 conv2d_22[0][0] ================================================================================================== Total params: 114,716 Trainable params: 114,716 Non-trainable params: 0 __________________________________________________________________________________________________
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

動画像における超解像手法/VESPCNの実装

概要 深層学習を用いた、動画像における超解像手法であるVESPCNの実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するVESPCNは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像手法は、3枚の連続するフレームを入力して、1枚の高解像度画像を出力します。 前半のMotion estimationが動き補償などのデータ前処理の部分、 後半のSpatio-temporal ESPCNが超解像を行う部分になっています。 それぞれの流れについてもう少し詳しく説明していきます。 ① Motion estimation 超解像アルゴリズムに入力する前のデータ前処理を行う箇所になります。 Motion estimation部分のアルゴリズムは以下の図の通りです。(図は論文から引用) このデータ前処理の部分では、2枚の連続したフレームを入力します。 ここでは、Coarse flow estimationとFine flow estimationという2つのニューラルネットワークを通してデータ前処理を行います。 $\Delta c$と$\Delta f$は移動量を表すパラメータみたいなもので、オプティカルフローのパラメータを求めるのと似ています。 今回は、このデータ前処理でもニューラルネットワークを使用しています。 過去の超解像手法では、オプティカルフローを事前に求めたり、Bicubic法などで補間処理を行っていたため、データ前処理にかなり時間がかかってました。 そのため、データ前処理にもニューラルネットワークを使用することで、計算速度を上げる狙いもあります。 ニューラルネットワークのパラメータは、以下の通りです。(図は論文から引用) kはフィルターのサイズ、nはフィルターの数、sはストライドの大きさを表しています。 それぞれ6つの層から構成されており、最後にupscaleで拡大しています。 この拡大の方法は、ESPCNで提案されたPixel shuffleを使用します。(図は論文から引用) 特徴としては、拡大したい倍率の2乗の値を入力する点です。 論文の例だと、5層でチャンネル数を32に拡張しています。 4倍拡大なので、4の2乗の16と2チャンネルというように分けることができ、4倍拡大された2チャンネルの結果を返します。 ② Spatio-temporal ESPCN Spatio-temporal ESPCNは、ESPCNを用いて、実際に超解像を行います。 ESPCNは、単一画像にのみ対応しているものでしたが、今回の論文では動画像に対応できるように入力チャンネル数を変更したりしています。 ESPCNの構造は前でも載せましたが、このようになっています。(図は論文から引用) ESPCNについては、以前に実装をしているので興味があればそちらもご覧ください。(実装記事) ESPCNで処理を行えば、結果が出力され本モデルの全ての工程が終わります。 3. 実装したアルゴリズム 今回は、VESPCNの全体を1つのニューラルネットワークのモデルとして実装してみました。 下の図が、本モデルの概要に近いと思います。(図は論文から引用) 入力する低解像度画像は3枚で、まずMotion estimationでデータ前処理を行います。 その後、ESPCNに3枚の画像を入力するという流れです。 コマンドラインでモデルを出力した例は、長すぎるので最後に参考として載せています。 4. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 5. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 6. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 160 #HRのサイズ train_width = 160 test_height = 720 test_width = 1280 train_dataset_num = 20000 #生成する学習用データの数 test_dataset_num = 10 #生成するテスト用データの数 train_cut_num = 10 #一組のフレームから生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画像のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 3 #入力するLRの数 input_channels = 1 #入力するLRのチャンネル数 mag = 4 #拡大倍率 MAX_BATSH_SIZE = 128 #最大のバッチサイズ EPOCHS = 1000 後は、学習のパラメータをあれこれ好きな値に設定します。85~107行目です。 今回は、バッチサイズの開始が1で、10epochごとに2倍にしていく、という構造だったため、少しコードを変更しています。 具体的にはMAX_BATCH_SIZEを2で割り続けて、2を何乗すればいいか計算し、その回数に応じて10回で学習させています。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) x = MAX_BATSH_SIZE i = 0 while x > 1: x /= 2 i += 1 for n in range(i): train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = 10, verbose = 2, batch_size = 2 ** n) train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = EPOCHS - 10 * i, verbose = 2, batch_size = MAX_BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は3枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 7. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:27.54 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) だいぶ粗さが取れているのが目視でもわかります。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみると、高解像度化がされているのが分かります。 実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得するとOKです。 8. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 9. まとめ 今回は、最近読んだ論文のVESPCNを元に実装してみました。 モデルが複雑すぎていつもより実装に時間がかかりましたが、無事に実装できました。 次は単一画像の超解像手法の実装をしてみようと思います。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 参考文献 ・Real-Time Video Super-Resolution with Spatio-Temporal Networks and Motion Compensation  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。 [参考]本モデルのコマンドライン出力 参考として、上記で軽く触れた本モデルのコマンドラインを紹介します。 (各フレームのMotion estimationなども全て1つのモデルにまとめているので入り乱れています。) 結構長めのモデルになってしまいました。 __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_t_minus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t_plus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_minus_1[0][0] __________________________________________________________________________________________________ concatenate_2 (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_plus_1[0][0] __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 2 1224 concatenate[0][0] __________________________________________________________________________________________________ conv2d_10 (Conv2D) (None, None, None, 2 1224 concatenate_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 2 5208 conv2d[0][0] __________________________________________________________________________________________________ conv2d_11 (Conv2D) (None, None, None, 2 5208 conv2d_10[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 2 14424 conv2d_1[0][0] __________________________________________________________________________________________________ conv2d_12 (Conv2D) (None, None, None, 2 14424 conv2d_11[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 2 5208 conv2d_2[0][0] __________________________________________________________________________________________________ conv2d_13 (Conv2D) (None, None, None, 2 5208 conv2d_12[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 6944 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_14 (Conv2D) (None, None, None, 3 6944 conv2d_13[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 2 0 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 2 0 conv2d_14[0][0] __________________________________________________________________________________________________ multiply (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] lambda[0][0] __________________________________________________________________________________________________ multiply_2 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] lambda_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem (Slici (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_1 (Sli (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_4 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_5 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.expand_dims (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem[0][0] __________________________________________________________________________________________________ tf.expand_dims_1 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_1[0][0] __________________________________________________________________________________________________ tf.expand_dims_4 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_4[0][0] __________________________________________________________________________________________________ tf.expand_dims_5 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_5[0][0] __________________________________________________________________________________________________ add (Add) (None, None, None, 1 0 tf.expand_dims[0][0] tf.expand_dims_1[0][0] __________________________________________________________________________________________________ add_5 (Add) (None, None, None, 1 0 tf.expand_dims_4[0][0] tf.expand_dims_5[0][0] __________________________________________________________________________________________________ add_1 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add[0][0] __________________________________________________________________________________________________ add_6 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_minus_1[0][0] lambda[0][0] add_1[0][0] __________________________________________________________________________________________________ concatenate_3 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_plus_1[0][0] lambda_2[0][0] add_6[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 2 3024 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_15 (Conv2D) (None, None, None, 2 3024 concatenate_3[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 2 5208 conv2d_5[0][0] __________________________________________________________________________________________________ conv2d_16 (Conv2D) (None, None, None, 2 5208 conv2d_15[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 2 5208 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_17 (Conv2D) (None, None, None, 2 5208 conv2d_16[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 2 5208 conv2d_7[0][0] __________________________________________________________________________________________________ conv2d_18 (Conv2D) (None, None, None, 2 5208 conv2d_17[0][0] __________________________________________________________________________________________________ conv2d_9 (Conv2D) (None, None, None, 8 1736 conv2d_8[0][0] __________________________________________________________________________________________________ conv2d_19 (Conv2D) (None, None, None, 8 1736 conv2d_18[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 2 0 conv2d_9[0][0] __________________________________________________________________________________________________ lambda_3 (Lambda) (None, None, None, 2 0 conv2d_19[0][0] __________________________________________________________________________________________________ add_2 (Add) (None, None, None, 2 0 lambda[0][0] lambda_1[0][0] __________________________________________________________________________________________________ add_7 (Add) (None, None, None, 2 0 lambda_2[0][0] lambda_3[0][0] __________________________________________________________________________________________________ multiply_1 (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] add_2[0][0] __________________________________________________________________________________________________ multiply_3 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] add_7[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_2 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_3 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_6 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_7 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_2 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_2[0][0] __________________________________________________________________________________________________ tf.expand_dims_3 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_6 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_6[0][0] __________________________________________________________________________________________________ tf.expand_dims_7 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_7[0][0] __________________________________________________________________________________________________ add_3 (Add) (None, None, None, 1 0 tf.expand_dims_2[0][0] tf.expand_dims_3[0][0] __________________________________________________________________________________________________ add_8 (Add) (None, None, None, 1 0 tf.expand_dims_6[0][0] tf.expand_dims_7[0][0] __________________________________________________________________________________________________ add_4 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add_3[0][0] __________________________________________________________________________________________________ add_9 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_8[0][0] __________________________________________________________________________________________________ concatenate_4 (Concatenate) (None, None, None, 3 0 add_4[0][0] input_t[0][0] add_9[0][0] __________________________________________________________________________________________________ conv2d_20 (Conv2D) (None, None, None, 9 684 concatenate_4[0][0] __________________________________________________________________________________________________ conv2d_21 (Conv2D) (None, None, None, 3 2624 conv2d_20[0][0] __________________________________________________________________________________________________ conv2d_22 (Conv2D) (None, None, None, 1 4624 conv2d_21[0][0] __________________________________________________________________________________________________ lambda_4 (Lambda) (None, None, None, 1 0 conv2d_22[0][0] ================================================================================================== Total params: 114,716 Trainable params: 114,716 Non-trainable params: 0 __________________________________________________________________________________________________
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超解像手法/VESPCNの実装

概要 深層学習を用いた、動画像における超解像手法であるVESPCNの実装したので、それのまとめの記事です。 Python + Tensorflowで実装を行いました。 今回紹介するコードはGithubにも載せています。 1. 超解像のおさらい 超解像について簡単に説明をします。 超解像とは解像度の低い画像に対して、解像度を向上させる技術のことです。 ここでいう解像度が低いとは、画素数が少なかったり、高周波成分(輪郭などの鮮鋭な部分を表す)がないような画像のことです。 以下の図で例を示します。(図は[論文]より引用) これは、超解像の説明をする時によく使われる画像です。 (a)は原画像、(b)は、画素数の少ない画像を見やすいように原画像と同じ大きさにした画像、(c)は、高周波成分を含まない画像の例です。 (b)と(c)は、荒かったりぼやけていたりしていると思います。 このような状態を解像度が低い画像といいます。 そして、超解像はこのような解像度が低い画像に処理を行い、(a)のような精細な画像を出力することを目的としています。 (今回紹介するVESPCNは、これを動画像に応用した超解像手法) 2. 論文の超解像アルゴリズム 超解像のアルゴリズムの概要図は以下の通りです。(図は論文から引用) この超解像手法は、3枚の連続するフレームを入力して、1枚の高解像度画像を出力します。 前半のMotion estimationが動き補償などのデータ前処理の部分、 後半のSpatio-temporal ESPCNが超解像を行う部分になっています。 それぞれの流れについてもう少し詳しく説明していきます。 ① Motion estimation 超解像アルゴリズムに入力する前のデータ前処理を行う箇所になります。 Motion estimation部分のアルゴリズムは以下の図の通りです。(図は論文から引用) このデータ前処理の部分では、2枚の連続したフレームを入力します。 ここでは、Coarse flow estimationとFine flow estimationという2つのニューラルネットワークを通してデータ前処理を行います。 $\Delta c$と$\Delta f$は移動量を表すパラメータみたいなもので、オプティカルフローのパラメータを求めるのと似ています。 今回は、このデータ前処理でもニューラルネットワークを使用しています。 過去の超解像手法では、オプティカルフローを事前に求めたり、Bicubic法などで補間処理を行っていたため、データ前処理にかなり時間がかかってました。 そのため、データ前処理にもニューラルネットワークを使用することで、計算速度を上げる狙いもあります。 ニューラルネットワークのパラメータは、以下の通りです。(図は論文から引用) kはフィルターのサイズ、nはフィルターの数、sはストライドの大きさを表しています。 それぞれ6つの層から構成されており、最後にupscaleで拡大しています。 この拡大の方法は、ESPCNで提案されたPixel shuffleを使用します。(図は論文から引用) 特徴としては、拡大したい倍率の2乗の値を入力する点です。 論文の例だと、5層でチャンネル数を32に拡張しています。 4倍拡大なので、4の2乗の16と2チャンネルというように分けることができ、4倍拡大された2チャンネルの結果を返します。 ② Spatio-temporal ESPCN Spatio-temporal ESPCNは、ESPCNを用いて、実際に超解像を行います。 ESPCNは、単一画像にのみ対応しているものでしたが、今回の論文では動画像に対応できるように入力チャンネル数を変更したりしています。 ESPCNの構造は前でも載せましたが、このようになっています。(図は論文から引用) ESPCNについては、以前に実装をしているので興味があればそちらもご覧ください。(実装記事) ESPCNで処理を行えば、結果が出力され本モデルの全ての工程が終わります。 3. 実装したアルゴリズム 今回は、VESPCNの全体を1つのニューラルネットワークのモデルとして実装してみました。 下の図が、本モデルの概要に近いと思います。(図は論文から引用) 入力する低解像度画像は3枚で、まずMotion estimationでデータ前処理を行います。 その後、ESPCNに3枚の画像を入力するという流れです。 コマンドラインでモデルを出力した例は、長すぎるので最後に参考として載せています。 4. 使用したデータセット 今回は、データセットにREDSを使用しました。 このデータセットは、動画像の超解像用のデータセットで、240種類の学習用データ、30種類の検証用データ、30種類のテスト用データの計300種類のデータセットです。 別の実装でもほとんどこのデータセットを使用しています。 パスの構造はこんな感じです。 train_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... val_sharp - 001 - フレーム100枚 - 002 - フレーム100枚 - ... このデータをBicubicで縮小したりしてデータセットを生成しました。 5. 画像評価指標PSNR 今回は、画像評価指標としてPSNRを使用しました。 PSNR とは Peak Signal-to-Noise Ratio(ピーク信号対雑音比) の略で、単位はデジベル (dB) で表せます。 PSNR は信号の理論ピーク値と誤差の2乗平均を用いて評価しており、8bit画像の場合、255(最大濃淡値)を誤差の標準偏差で割った値です。 今回は、8bit画像を使用しましたが、計算量を減らすため、全画素値を255で割って使用しました。 そのため、最小濃淡値が0で最大濃淡値が1です。 dB値が高いほど拡大した画像が元画像に近いことを表します。 PSNRの式は以下のとおりです。 PSNR = 10\log_{10} \frac{1^2 * w * h}{\sum_{x=0}^{w-1}\sum_{y=0}^{h-1}(p_1(x,y) - p_2(x,y))^2 } なお、$w$は画像の幅、$h$は画像の高さを表しており、$p_1$は元画像、$p_2$はPSNRを計測する画像を示しています。 6. コードの使用方法 このコード使用方法は、自分が執筆した別の実装記事とほとんど同じです。 ① 学習データ生成 まず、Githubからコードを一式ダウンロードして、カレントディレクトリにします。 Windowsのコマンドでいうとこんな感じ。 C:~/keras_RVSR> 次に、main.pyから生成するデータセットのサイズ・大きさ・切り取る枚数、ファイルのパスなどを指定します。 main.pyの15~26行目です。 使うPCのメモリ数などに応じで、画像サイズや学習データ数の調整が必要です。 main.py train_height = 160 #HRのサイズ train_width = 160 test_height = 720 test_width = 1280 train_dataset_num = 20000 #生成する学習用データの数 test_dataset_num = 10 #生成するテスト用データの数 train_cut_num = 10 #一組のフレームから生成するデータの数 test_cut_num = 1 train_movie_path = "../../reds/train_sharp" #動画像のフレームが入っているパス test_movie_path = "../../reds/val_sharp" 指定したら、コマンドでデータセットの生成をします。 C:~/keras_RVSR>python main.py --mode train_datacreate これで、train_data_list.npzというファイルのデータセットが生成されます。 ついでにテストデータも同じようにコマンドで生成します。コマンドはこれです。 C:~/keras_RVSR>python main.py --mode test_datacreate ② 学習 次に学習を行います。 設定するパラメータの箇所は、epoch数と学習率とかですかね... まずは、main.pyの28~33行目 main.py input_LR_num = 3 #入力するLRの数 input_channels = 1 #入力するLRのチャンネル数 mag = 4 #拡大倍率 MAX_BATSH_SIZE = 128 #最大のバッチサイズ EPOCHS = 1000 後は、学習のパラメータをあれこれ好きな値に設定します。85~107行目です。 今回は、バッチサイズの開始が1で、10epochごとに2倍にしていく、という構造だったため、少しコードを変更しています。 具体的にはMAX_BATCH_SIZEを2で割り続けて、2を何乗すればいいか計算し、その回数に応じて10回で学習させています。 main.py optimizers = tf.keras.optimizers.Adam(learning_rate=1e-4) train_model.compile(loss = "mean_squared_error", optimizer = optimizers, metrics = [psnr]) x = MAX_BATSH_SIZE i = 0 while x > 1: x /= 2 i += 1 for n in range(i): train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = 10, verbose = 2, batch_size = 2 ** n) train_model.fit({"input_t_minus_1":train_x[0], "input_t":train_x[1], "input_t_plus_1":train_x[2]}, train_y, epochs = EPOCHS - 10 * i, verbose = 2, batch_size = MAX_BATSH_SIZE) optimizerはAdam、損失関数は最小二乗法を使用しています。 入力画像は今回は3枚で出力は1枚です。 学習はデータ生成と同じようにコマンドで行います。 C:~/keras_RVSR>python main.py --mode train_model これで、学習が終わるとモデルが出力されます。 ③ 評価 最後にモデルを使用してテストデータで評価を行います。 これも同様にコマンドで行いますが、事前に①でテストデータも生成しておいてください。 C:~/keras_RVSR>python main.py --mode evaluate このコマンドで、画像を出力してくれます。 7. 結果 出力した画像はこのようになりました。 なお、今回は輝度値のみで学習を行っているため、カラー画像には対応していません。 対応させる場合は、modelのInputのchannel数を変えたり、データセット生成のchannel数を変える必要があります。 元画像 低解像度画像(4倍縮小) 生成画像 PSNR:27.54 分かりにくいので、低解像度画像を拡大にして生成画像と同じサイズにしたものも載せておきます。 低解像度画像(生成画像と同じサイズに拡大) だいぶ粗さが取れているのが目視でもわかります。 最後に元画像・低解像度画像・生成画像の一部を並べて表示してみます。 並べてみると、高解像度化がされているのが分かります。 実際の動画像に処理をかける場合は、動画像をフレームに分解して、1枚ずつ処理を行う必要があります。 OpenCVで動画像をフレームごとに取得するとOKです。 8. コードの全容 前述の通り、Githubに載せています。 pythonのファイルは主に3つあります。 各ファイルの役割は以下の通りです。 data_create.py : データ生成に関するコード。 model.py : 超解像のアルゴリズムに関するコード。 main.py : 主に使用するコード。 9. まとめ 今回は、最近読んだ論文のVESPCNを元に実装してみました。 モデルが複雑すぎていつもより実装に時間がかかりましたが、無事に実装できました。 次は単一画像の超解像手法の実装をしてみようと思います。 記事が長くなってしまいましたが、最後まで読んでくださりありがとうございました。 参考文献 ・Real-Time Video Super-Resolution with Spatio-Temporal Networks and Motion Compensation  今回実装の参考にした論文。 ・画素数の壁を打ち破る 複数画像からの超解像技術  超解像の説明のために使用。 ・REDSのデータセット  今回使用したデータセット。 [参考]本モデルのコマンドライン出力 参考として、上記で軽く触れた本モデルのコマンドラインを紹介します。 (各フレームのMotion estimationなども全て1つのモデルにまとめているので入り乱れています。) 結構長めのモデルになってしまいました。 __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_t_minus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ input_t_plus_1 (InputLayer) [(None, None, None, 0 __________________________________________________________________________________________________ concatenate (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_minus_1[0][0] __________________________________________________________________________________________________ concatenate_2 (Concatenate) (None, None, None, 2 0 input_t[0][0] input_t_plus_1[0][0] __________________________________________________________________________________________________ conv2d (Conv2D) (None, None, None, 2 1224 concatenate[0][0] __________________________________________________________________________________________________ conv2d_10 (Conv2D) (None, None, None, 2 1224 concatenate_2[0][0] __________________________________________________________________________________________________ conv2d_1 (Conv2D) (None, None, None, 2 5208 conv2d[0][0] __________________________________________________________________________________________________ conv2d_11 (Conv2D) (None, None, None, 2 5208 conv2d_10[0][0] __________________________________________________________________________________________________ conv2d_2 (Conv2D) (None, None, None, 2 14424 conv2d_1[0][0] __________________________________________________________________________________________________ conv2d_12 (Conv2D) (None, None, None, 2 14424 conv2d_11[0][0] __________________________________________________________________________________________________ conv2d_3 (Conv2D) (None, None, None, 2 5208 conv2d_2[0][0] __________________________________________________________________________________________________ conv2d_13 (Conv2D) (None, None, None, 2 5208 conv2d_12[0][0] __________________________________________________________________________________________________ conv2d_4 (Conv2D) (None, None, None, 3 6944 conv2d_3[0][0] __________________________________________________________________________________________________ conv2d_14 (Conv2D) (None, None, None, 3 6944 conv2d_13[0][0] __________________________________________________________________________________________________ lambda (Lambda) (None, None, None, 2 0 conv2d_4[0][0] __________________________________________________________________________________________________ lambda_2 (Lambda) (None, None, None, 2 0 conv2d_14[0][0] __________________________________________________________________________________________________ multiply (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] lambda[0][0] __________________________________________________________________________________________________ multiply_2 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] lambda_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem (Slici (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_1 (Sli (None, None, None) 0 multiply[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_4 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_5 (Sli (None, None, None) 0 multiply_2[0][0] __________________________________________________________________________________________________ tf.expand_dims (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem[0][0] __________________________________________________________________________________________________ tf.expand_dims_1 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_1[0][0] __________________________________________________________________________________________________ tf.expand_dims_4 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_4[0][0] __________________________________________________________________________________________________ tf.expand_dims_5 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_5[0][0] __________________________________________________________________________________________________ add (Add) (None, None, None, 1 0 tf.expand_dims[0][0] tf.expand_dims_1[0][0] __________________________________________________________________________________________________ add_5 (Add) (None, None, None, 1 0 tf.expand_dims_4[0][0] tf.expand_dims_5[0][0] __________________________________________________________________________________________________ add_1 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add[0][0] __________________________________________________________________________________________________ add_6 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_5[0][0] __________________________________________________________________________________________________ concatenate_1 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_minus_1[0][0] lambda[0][0] add_1[0][0] __________________________________________________________________________________________________ concatenate_3 (Concatenate) (None, None, None, 5 0 input_t[0][0] input_t_plus_1[0][0] lambda_2[0][0] add_6[0][0] __________________________________________________________________________________________________ conv2d_5 (Conv2D) (None, None, None, 2 3024 concatenate_1[0][0] __________________________________________________________________________________________________ conv2d_15 (Conv2D) (None, None, None, 2 3024 concatenate_3[0][0] __________________________________________________________________________________________________ conv2d_6 (Conv2D) (None, None, None, 2 5208 conv2d_5[0][0] __________________________________________________________________________________________________ conv2d_16 (Conv2D) (None, None, None, 2 5208 conv2d_15[0][0] __________________________________________________________________________________________________ conv2d_7 (Conv2D) (None, None, None, 2 5208 conv2d_6[0][0] __________________________________________________________________________________________________ conv2d_17 (Conv2D) (None, None, None, 2 5208 conv2d_16[0][0] __________________________________________________________________________________________________ conv2d_8 (Conv2D) (None, None, None, 2 5208 conv2d_7[0][0] __________________________________________________________________________________________________ conv2d_18 (Conv2D) (None, None, None, 2 5208 conv2d_17[0][0] __________________________________________________________________________________________________ conv2d_9 (Conv2D) (None, None, None, 8 1736 conv2d_8[0][0] __________________________________________________________________________________________________ conv2d_19 (Conv2D) (None, None, None, 8 1736 conv2d_18[0][0] __________________________________________________________________________________________________ lambda_1 (Lambda) (None, None, None, 2 0 conv2d_9[0][0] __________________________________________________________________________________________________ lambda_3 (Lambda) (None, None, None, 2 0 conv2d_19[0][0] __________________________________________________________________________________________________ add_2 (Add) (None, None, None, 2 0 lambda[0][0] lambda_1[0][0] __________________________________________________________________________________________________ add_7 (Add) (None, None, None, 2 0 lambda_2[0][0] lambda_3[0][0] __________________________________________________________________________________________________ multiply_1 (Multiply) (None, None, None, 2 0 input_t_minus_1[0][0] add_2[0][0] __________________________________________________________________________________________________ multiply_3 (Multiply) (None, None, None, 2 0 input_t_plus_1[0][0] add_7[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_2 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_3 (Sli (None, None, None) 0 multiply_1[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_6 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.__operators__.getitem_7 (Sli (None, None, None) 0 multiply_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_2 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_2[0][0] __________________________________________________________________________________________________ tf.expand_dims_3 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_3[0][0] __________________________________________________________________________________________________ tf.expand_dims_6 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_6[0][0] __________________________________________________________________________________________________ tf.expand_dims_7 (TFOpLambda) (None, None, None, 1 0 tf.__operators__.getitem_7[0][0] __________________________________________________________________________________________________ add_3 (Add) (None, None, None, 1 0 tf.expand_dims_2[0][0] tf.expand_dims_3[0][0] __________________________________________________________________________________________________ add_8 (Add) (None, None, None, 1 0 tf.expand_dims_6[0][0] tf.expand_dims_7[0][0] __________________________________________________________________________________________________ add_4 (Add) (None, None, None, 1 0 input_t_minus_1[0][0] add_3[0][0] __________________________________________________________________________________________________ add_9 (Add) (None, None, None, 1 0 input_t_plus_1[0][0] add_8[0][0] __________________________________________________________________________________________________ concatenate_4 (Concatenate) (None, None, None, 3 0 add_4[0][0] input_t[0][0] add_9[0][0] __________________________________________________________________________________________________ conv2d_20 (Conv2D) (None, None, None, 9 684 concatenate_4[0][0] __________________________________________________________________________________________________ conv2d_21 (Conv2D) (None, None, None, 3 2624 conv2d_20[0][0] __________________________________________________________________________________________________ conv2d_22 (Conv2D) (None, None, None, 1 4624 conv2d_21[0][0] __________________________________________________________________________________________________ lambda_4 (Lambda) (None, None, None, 1 0 conv2d_22[0][0] ================================================================================================== Total params: 114,716 Trainable params: 114,716 Non-trainable params: 0 __________________________________________________________________________________________________
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL2でNvidia-Docker使えなかったから、そのままGPU環境作ってみた

はじめに [2021/05/01現在] 以前まではWSL2上のNvidia-DockerでGPU認識していたのですが、いつの間にか認識しなくなっていたので環境を作り直しました。 新しい環境をつくってみたもののNvidia-Dockerのサンプルがエラーで動かなかったため、WSL2上にGPUが使える環境を作りました。 この記事では、自分が試したことを備忘録的に以下の流れで記載しています 1. NVIDIAの公式手順を試した 2. Nvidia-Dockerのサンプルが動かない 3. 色々調べてみた 4. Nvidia-Dockerを使わずにWSL2上にGPU環境を構築した 環境 Windows10 Home Insider Preview build:21370.1 Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz 3.60 GHz RAM: 32GB RTX2080Ti 一応Windows側のCuda周り CUDA 10.1 CuDNN 7.6.5 CUDA on WSLに沿って環境構築してみる NVIDIAの公式手順を初めからなぞってみました。 WSL2はUbuntu20.04がインストール済みです。 1. CUDA Driveのインストール ここからCUDA on WSL2用のCUDA Driverをダウンロードして、インストール 2. WSL2上でCUDA Toolkitをセッティング(sudoが必要かもしれません) $ apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub $ sh -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/cuda.list' $ apt-get update $ apt-get install -y cuda-toolkit-11-0 3. Dockerのセットアップ $ curl https://get.docker.com | sh $ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) $ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - $ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list $ curl -s -L https://nvidia.github.io/libnvidia-container/experimental/$distribution/libnvidia-container-experimental.list | sudo tee /etc/apt/sources.list.d/libnvidia-container-experimental.list $ sudo apt-get update $ sudo apt-get install -y nvidia-docker2 Nvidia-Dockerのサンプルを試してみる 公式に記載のあるサンプルを動かしてみる $ sudo docker run --gpus all nvcr.io/nvidia/k8s/cuda-sample:nbody nbody -gpu -benchmark Unable to find image 'nvcr.io/nvidia/k8s/cuda-sample:nbody' locally nbody: Pulling from nvidia/k8s/cuda-sample d519e2592276: Pull complete d22d2dfcfa9c: Pull complete b3afe92c540b: Pull complete b25f8d7adb24: Pull complete ddb025f124b9: Pull complete fe72fda9c19e: Pull complete c6a265e4ffa3: Pull complete c931a9542ebf: Pull complete f7eb321dd245: Pull complete d67fd954fbd5: Pull complete Digest: sha256:a2117f5b8eb3012076448968fd1790c6b63975c6b094a8bd51411dee0c08440d Status: Downloaded newer image for nvcr.io/nvidia/k8s/cuda-sample:nbody docker: Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: process_linux.go:495: container init caused: Running hook #0:: error running hook: exit status 1, stdout: , stderr: nvidia-container-cli: initialization error: driver error: failed to process requ est: unknown. ERRO[0012] error waiting for container: context canceled nvidia-container-cliとdriverでエラーが出てるっぽい... 原因調査 調べてみると、同じような現象が発生している模様。 https://github.com/NVIDIA/nvidia-docker/issues/1203 https://forums.developer.nvidia.com/t/stderr-nvidia-container-cli-initialization-error-driver-error-failed-to-process-request-n-unknown/128871/4 https://github.com/NVIDIA/nvidia-docker/issues/1409 他にも、WSL2自体を初期化してcuda-toolkit-11-0をcuda-toolkit-10-2などにしても動かない... WSL2のバージョンアップなども試しましたが、結局動かないので諦めました。 Nvidia-Dockerを諦めて、WSL2上でGPUを使えるようにする 使いたいTensorflowのバージョンの兼ね合いから、CUDA10.1、CuDNN 7.6.5で環境を構築しました。 Python環境にはpipenvを利用します。 1. WSL2の初期化 Windowsの「設定」「アプリと機能」からUbuntuをアンインストールし、「Microsoft Store」から再インストール 2. WSL2上でCUDA Toolkitをセッティング $ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub $ sudo sh -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/cuda.list' $ sudo apt-get update $ sudo apt-get install -y cuda-toolkit-10-1 3. libcublas.so.10にパスを通す cuda-10.1にはlibcublas.so.10は存在しないので、cuda-10.2の方にパスを通しました。 .bashrcに以下を追記してパスを通す。 export LD_LIBRARY_PATH=/usr/local/cuda-10.2/lib64:${LD_LIBRARY_PATH} 追記した後に以下コマンドを実行して反映する。 $ source ~/.bashrc 4. cuDNNのインストール cuDNN Downloadから自分のCUDAのバージョンに対応したものをダウンロード・インストールします。 $ wget ${ダウンロードURL} $ sudo dpkg -i ${ダウンロードしたファイル名} 5. Python環境の構築 pipenvのセッティング $ sudo apt update && sudo apt install -y --no-install-recommends \ build-essential \ libffi-dev \ libssl-dev \ zlib1g-dev \ libbz2-dev \ libreadline-dev \ libsqlite3-dev \ git $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv $ touch ~/.bash_profile $ echo -e "# pyenv paths" >> ~/.bash_profile $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile $ pyenv -v $ pyenv install 3.7.4 $ pyenv global 3.7.4 $ pip install pipenv 6. 試してみる お試し環境を作成 $ mkdir {お試し用ディレクトリ} $ cd {お試し用ディレクトリ} $ pipenv install tensorflow==2.3 TensorflowでGPUが認識しているか確認する $ pipenv shell $ python >>> from tensorflow.python.client import device_lib 2021-05-01 15:00:32.024423: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1 >>> device_lib.list_local_devices() ...{出力が多いので割愛}... [name: "/device:CPU:0" device_type: "CPU" memory_limit: 268435456 locality { } incarnation: 155588733005698279 , name: "/device:XLA_CPU:0" device_type: "XLA_CPU" memory_limit: 17179869184 locality { } incarnation: 11516640570978184874 physical_device_desc: "device: XLA_CPU device" , name: "/device:XLA_GPU:0" device_type: "XLA_GPU" memory_limit: 17179869184 locality { } incarnation: 3037529886568359493 physical_device_desc: "device: XLA_GPU device" , name: "/device:GPU:0" device_type: "GPU" memory_limit: 9849037120 locality { bus_id: 1 links { } } incarnation: 2702533884710410519 physical_device_desc: "device: 0, name: NVIDIA GeForce RTX 2080 Ti, pci bus id: 0000:01:00.0, compute capability: 7.5" ] これで一応Tensorflow側からGPUが認識できていることを確認することができました。 おわりに 今回は、Nvidia-Dockerを使った環境構築を諦め、WSL2上にそのままGPU環境を作りました。 本当はちゃんとDockerを使って色々試したかったのですが、何か解決策が見つかるまでこれで我慢しようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む