20201204のTensorFlowに関する記事は3件です。

TecoGAN 試してみた(私的備忘録・メモ帳) -(1)

はじめに

はじめまして
現在、深層学習・画像処理を趣味でやっている学生です。最近巷で流行っている(いた?)TecoGANを試してみました。

※あくまで、私的な備忘録です。もし間違いなどありましたら、指摘して下さると幸いです。。

TecoGANとは?

深層学習を利用した動画(ビデオ)超解像手法の一つ。
元論文 https://arxiv.org/pdf/1811.09393.pdf

GANを利用しているため、生成結果画像が人の知覚品質に近いものになるようです。
単眼(単一)超解像SRGANの動画版みたいです。

超解像とは?

そもそも超解像とは何か?としばしば聞かれることがあるのでここにて記載しておきます。

超解像とはその名の通り画像を高解像度化させる処理です。
高解像度とは、画質がいい、鮮明に見えるなどいろいろありますが、ここでは、画素数が大きい+高周波成分を含んでいるという意味です。

低解像度画像 81×54 px 超解像処理後の画像 810×540 px

この超解像ですが、いろいろな種類があります。
画像一枚を高解像度化する単一(単眼)超解像、動画を(各フレーム全て)高解像度化する動画(ビデオ)超解像など色々な種類・手法がありますが、今回のTecoGANは、深層学習を用いた後者のタイプになります。

環境構築

PC環境

OS : Ubuntu 18.04
CPU: i7
GPU: Geforce GTX1080ti
python 3.6

各種ライブラリの導入

https://github.com/thunil/TecoGAN
こちらがソースもとになります。
ソースもとの記載通りに、まず以下コマンドでtensorflownをインストールします。

pip3 install --ignore-installed --upgrade tensorflow-gpu

次にrequirements.txtに記載されている各ライブラリのインストールを行います。

pip3 install -r requirements.txt

よしこれで実装できるぞ!と思ったのですが、上手く実行できませんでした・・・(笑)
それもそのはずで、ソースが公開されたのが2年くらい前で、tensorflow-gpu,requirements.txtとともに当時に合わせて、ver〇.〇.〇以上をインストールと記載されているからです。

しかし、現在(2020年/10月)において、それらをインストールすると、最新のものがインストールされ、結果として、ライブラリが最新バージョンだと仕様が変更されていたり、ライブラリ同士が噛み合わなかったりして、上手く実行できないようです。

なので参考までに以下にて自分が推論・評価・学習のすべてのモードにて実行可能であったcuda,cudnn,tensorflow-gpu,requirements.txt各ライブラリのバージョンの組み合わせを載せておきます。

cuda
cuda : v10.0
cudnn
cudnn : 7.4.1
tensorflow-gpu
tensorflow-gpu==1.13.1

requirements.txt 
numpy==1.16.2
scipy==1.0.1
scikit-image==0.14.0
matplotlib==3.0.3
pandas==0.23.1
Keras==2.1.2
torch==1.2.0
torchvision==0.4.0
opencv-python==3.1.0.5
ipython==7.4.0

その他ライブラリ・まとめ
Screenshot from 2020-09-29 15-58-43-2.png

TecoGAN実行

学習済みモデル(とサンプル用画像)のダウンロード

以下コマンドで学習済みモデルとサンプル用連番画像(Vid4など)の低解像度(LR)、高解像度(HR)のセットをダウンロードします。

python3 runGan.py 0

ダウンロード自体は、自分の環境においては数十分程かかった気がします。

推論モード

以下コマンドの実行でTecoGAN超解像の推論を実行できます。デフォルトでは、LRフォルダ中のcalendarが入力となっています。

python3 runGan.py 1

結果

・calendar

LR(Input) 180×144 px TecoGAN(Output) 720×576 px

評価モード

以下コマンドの実行で画像の定量評価が行えます。

python3 runGan.py 2

定量評価指標には、フレーム一枚を評価する空間評価指標とフレーム間のつながりを評価する時間評価指標の二種類、計5つあります。

空間評価指標 時間評価指標
(ⅰ)PSNR (ⅱ)SSIM (ⅲ)LPIPS (ⅳ)tOF (ⅴ)tLP

LPIPSは、論文曰く、生成画像の多様性を評価する知覚的評価指標であり、値が低いほど知覚品質において良いとされているらしいです。

tOFは、これも論文曰く、正解フレーム間と生成フレーム間の各フレーム間の動き(optical flow)の類似度を評価するものらしいです。

tLPは、LPIPSにより、正解フレーム間と生成フレーム間の知覚的類似性を測定する?ものらしいです。(これだけいまいちピンときませんでした。)

終わり

TecoGANは自分が想像していた以上に出来栄えがよく素晴らしいものだと感じました!

ですが、他の方がすでにご指摘されている通り、4倍拡大にしか対応していなく、他の倍率に変えることはできないのかなと思いました。
また、デフォルトの入力動画だと、動きが緩やかなものしかないですが、動きが速い動画の場合は、オプティカルフローを求める処理など上手く対応できているのかな。。。なんてことも疑問に思いました。

まあ何はともあれ、実装できてよかったです。
次回は、ネットワークの構造など理論的なことや学習モードについてまとめてみようと思います!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlow 2.4.0-rc3のビルド手順(Windows10, CUDA11.1.1, cuDNN 8.0.5, Python 3.9.0)

2020/11/30時点でのTensorFlow 2.4.0-rc3のビルド方法の概要を残しておきます。

毎回同じような手順でビルドできるようになってきていますが、その時々で注意すべきポイントや手順があったりします。今回の組み合わせでは、Numpyのバージョンにだけ気をつければ正常にビルドできました。ビルド時点でNumpyの最新バージョンは1.19.4でしたが、このバージョンはWindows環境だとNumpyをimportするだけでエラーになるので、1つ前の1.19.3を使用します。

追伸

2020/12/05

私の環境だけかもしれませんが、以下環境の組み合わせで利用すると作成したTensorFlowのwheelパッケージをインストールする際にFailed to build h5py grpcioとなりインストールに失敗します。
バージョンはh5py 3.1.0grpcio 1.34.0になりますが、現時点で解決策は調べられていません。

2020/12/06

作成したTensorFlowのwheelパッケージのインストールで失敗する件、Python 3.9.0を利用したときの依存パッケージの組み合わせなどに起因しているエラーのように見えます。
全く同じ設定でPython 3.8.6を利用したら問題ありませんでした。

環境情報

ビルドに利用した環境です。CUDAやcuDNNのフォルダなど、事前にパスを通した状態になっています。

  • Windows 10 Pro 20H2 (64bit)
  • Visual Studio Community 2019 Ver 16.8.2
  • Python 3.9.0 Python 3.8.6(3.9.0を使うとwheelパッケージインストール時にエラーになります)
  • MSYS2(pacman -S git patch unzip で必要なパッケージを導入済み)
  • Bazel 3.7.1 (3.1.0以上のバージョンを使う必要あり)
  • CUDA 11.1.1
  • cuDNN 8.0.5.39

ビルド用のフォルダ構成など

今回はS:\build\build_tf240rc3 フォルダ配下にTensorFlowのソースコードをダウンロードしてビルドしています。Pythonの仮想環境も、TensorFlowビルド用に用意します。

S:/build/build_tf240rc3  # 作業フォルダRoot
 + tensorflow  # gitで取得してくるソースコード
 + venv        # Python仮想環境
 + wheelhouse  # 作成したwhlファイルを格納するフォルダ

ビルド手順

x64 Native Tools Command Prompt for VS 2019 を起動して以下の手順でビルドを行います。

# 仮想環境を作成して有効化する
python -m venv s:\build\build_tf240rc3\venv
cd /d s:\build\build_tf240rc3
.\venv\Scripts\activate.bat

# 必要なパッケージのインストール
# 注意:Numpyは最新の1.19.4を使うとエラーになるので1.19.3を使用(利用時もNumpy 1.19.3を使用すること)
python -m pip install --upgrade pip
pip install numpy==1.19.3
pip install six wheel
pip install keras_applications==1.0.8 --no-deps
pip install keras_preprocessing==1.1.2 --no-deps

# ソースコード取得(v2.4.0-rc3のタグ指定)
git clone -b v2.4.0-rc3 https://github.com/tensorflow/tensorflow.git
cd tensorflow

# 環境によってはコマンドのパラメーターが長くなりすぎてエラーになるので不要な環境変数を削除
set _OLD_VIRTUAL_PATH=

# ビルド構成の設定
# CUDA support: Y
# CUDA compute capabilities: 7.5 (利用環境に合わせて変更)
# Optimization: /arch:AVX2 (利用環境に合わせて変更)
# それ以外はデフォルト設定(Enter)
python ./configure.py

# ビルド
# TensorFlow 2.3.0+CUDA11の場合は「DTHRUST_IGNORE_CUB_VERSION_CHECK」のおまじない(CUB互換チェックのスキップ)が必要だったが今回は不要
bazel build --config=opt --config=avx2_win --config=short_logs --config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow/tools/pip_package:build_pip_package

# パッケージの作成(wheelhouseフォルダにパッケージを作成)
# 数分間画面が更新されないので心配になりますが、きちんと処理されているのでしばらく待ちましょう
bazel-bin\tensorflow\tools\pip_package\build_pip_package ..\wheelhouse

これで完了です。
なおビルドに使用したbazelの中間ファイル等は%UserProfile%\_bazel_%UserName%フォルダに作成され、容量も20GB近くになるので、不要であれば削除しても問題ありません。Bazelが起動していると削除できないので、その場合はコマンドプロンプトでbazel shutdownとして、Bazelを終了してから削除してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlow/KerasでAdaBeliefを実装してみた

はじめに

AdaBeliefという最適化アルゴリズムが発表されたらしいので、実際に実装して評価してみた。
以前、TensorFlow/KerasでのOptimizerの実装方法を記事にしたのだが、こちらはその続編でもある。この辺に興味のない方は実験結果だけみてもらえればよい。

環境

  • TensorFlow 2.3.0
  • Google Colab

まずはMomentumSGDから

ベースとなるMomentumSGDのコードを載せる。
前回の記事で作成したものと基本的には同じだが改造にむけて若干の修正が入っている。複数のアルゴリズムが実装されることになるので、MultiOptimizerV1とした。この時点ではVanillaSGDとMomentumSGDが実装ずみ。

MultiOptimizerV1
from tensorflow.python.keras.optimizer_v2.optimizer_v2 import OptimizerV2
class MultiOptimizerV1(OptimizerV2):
    def __init__(self, learning_rate=0.01, momentum=0.0, centered=True, name='MultiOptimizerV1', **kwargs):
        super().__init__(name, **kwargs)
        self._set_hyper('learning_rate', kwargs.get('lr', learning_rate))
        self._set_hyper('decay', self._initial_decay)
        self.momentum = momentum
        self.centered = centered if momentum!=0.0 else False

    def get_config(self):
        config = super(MultiOptimizerV1, self).get_config()
        config.update({
            'learning_rate': self._serialize_hyperparameter('learning_rate'),
            'decay': self._serialize_hyperparameter('decay'),
            'momentum': self.momentum,
            'centered': self.centered,
        })
        return config
    def _create_slots(self, var_list):
        for var in var_list:
            if self.momentum!=0.0:
                self.add_slot(var, 'm')

    def _resource_apply_dense(self, grad, var, apply_state=None):
        var_device, var_dtype = var.device, var.dtype.base_dtype
        lr_t = self._decayed_lr(var_dtype)
        local_step = tf.cast(self.iterations+1, var_dtype)
        updates = []

        if self.momentum!=0.0:
            # MomentumSGD
            m = self.get_slot(var, 'm')
            m_t = m.assign( self.momentum*m + (1.0-self.momentum)*grad )
            if self.centered:
                denominator = 1.0-(self.momentum**local_step)
                m_t_hat = m_t / denominator
            else:
                m_t_hat = m_t
            updates.append(m_t)
        else:
            m_t_hat = grad

        # Update var
        var_update = var.assign(var - lr_t*m_t_hat)
        updates.append(var_update)
        return tf.group(*updates)

    def _resource_apply_sparse(self, grad, var, indices):
        raise NotImplementedError("not implemented")

RMSpropを経由してAdamへ

MultiOptimizerV1でmomentum=0とするとVanillaSGDなのだが、その学習率を適応的に更新するのがRMSpropなので、コードは共通で機能追加できる。
以下、追加したものがMultiOptimizerV2。

MultiOptimizerV2
from tensorflow.python.keras.optimizer_v2.optimizer_v2 import OptimizerV2
from tensorflow.python.keras import backend_config

class MultiOptimizerV2(OptimizerV2):
    def __init__(self, learning_rate=0.01, momentum=0.0, centered=True, rho=0.0, centered_v=True, epsilon=1e-7, name='MultiOptimizerV2', **kwargs):
        super().__init__(name, **kwargs)
        self._set_hyper('learning_rate', kwargs.get('lr', learning_rate))
        self._set_hyper('decay', self._initial_decay)
        self.momentum = momentum
        self.centered = centered if momentum!=0.0 else False
        self.rho = rho
        self.centered_v = centered_v if rho!=0.0 else False
        self.epsilon = epsilon or backend_config.epsilon()

    def get_config(self):
        config = super(MultiOptimizerV2, self).get_config()
        config.update({
            'learning_rate': self._serialize_hyperparameter('learning_rate'),
            'decay': self._serialize_hyperparameter('decay'),
            'momentum': self.momentum,
            'centered': self.centered,
            'rho': self.rho,
            'centered_v': self.centered_v,
            'epsilon': self.epsilon,
        })
        return config
    def _create_slots(self, var_list):
        for var in var_list:
            if self.momentum!=0.0:
                self.add_slot(var, 'm')
            if self.rho!=0.0:
                self.add_slot(var, 'v')

    def _resource_apply_dense(self, grad, var, apply_state=None):
        var_device, var_dtype = var.device, var.dtype.base_dtype
        lr_t = self._decayed_lr(var_dtype)
        local_step = tf.cast(self.iterations+1, var_dtype)

        updates = []
        if self.momentum!=0.0:
            # MomentumSGD
            m = self.get_slot(var, 'm')
            m_t = m.assign( self.momentum*m + (1.0-self.momentum)*grad )
            if self.centered:
                denominator = 1.0-(self.momentum**local_step)
                m_t_hat = m_t / denominator
            else:
                m_t_hat = m_t
            updates.append(m_t)
        else:
            m_t_hat = grad

        if self.rho!=0.0:
            #RMSprop
            v = self.get_slot(var, 'v')
            v_t = v.assign( self.rho*v + (1.0-self.rho)*(grad**2) )
            if self.centered_v:
                denominator = 1.0-(self.rho**local_step)
                lr_t = lr_t / (((v_t/denominator)**0.5)+self.epsilon)
            else:
                lr_t = lr_t / ((v_t**0.5)+self.epsilon)
            updates.append(v_t)

        # Update var
        var_update = var.assign(var - lr_t*m_t_hat)
        updates.append(var_update)
        return tf.group(*updates)

    def _resource_apply_sparse(self, grad, var, indices):
        raise NotImplementedError("not implemented")

パラメータにRMSProp同様にrhoとepslionが追加された。追加でcentered_vというのも追加されているが、これはバイアス補正をするフラグ。
新たに内部の計算で使用するためにadd_slotで'v'を追加している。

これでRMSpropが実装されたわけだが、ここでmomentumを設定するとAdamになる。beta_1がmomentum、beta_2がrhoにあたる。
アルゴリズムとパラメータは下記のような関係になっている。

Algorithm momentum centered rho centered_v
VanillaSGD =0.0 False =0.0 False
MomenutumSGD >0.0 False =0.0 False
RMSprop =0.0 False >0.0 False
Adam >0.0 True >0.0 True

TensorFlow/KerasのSGDの実装ではmomentumを設定すると他のアルゴリズムと学習率の対応が一致しなくなるが、この実装ではそういうことは起きない。

そしてAdaBelief

詳細はこちらの記事で。ここでは論文に記載されている数式をもとに実装。

MultiOptimizerV3
from tensorflow.python.keras.optimizer_v2.optimizer_v2 import OptimizerV2
from tensorflow.python.keras import backend_config
class MultiOptimizerV3(OptimizerV2):
    def __init__(self, learning_rate=0.01, momentum=0.0, centered=True, rho=0.0, centered_v=True, epsilon=1e-7, belief=False, name='MultiOptimizerV3', **kwargs):
        super().__init__(name, **kwargs)
        self._set_hyper('learning_rate', kwargs.get('lr', learning_rate))
        self._set_hyper('decay', self._initial_decay)
        self.momentum = momentum
        self.centered = centered if momentum!=0.0 else False
        self.rho = rho
        self.centered_v = centered_v if rho!=0.0 else False
        self.epsilon = epsilon or backend_config.epsilon()
        self.belief = belief if rho!=0.0 and momentum!=0.0 else False

    def get_config(self):
        config = super(MultiOptimizerV3, self).get_config()
        config.update({
            'learning_rate': self._serialize_hyperparameter('learning_rate'),
            'decay': self._serialize_hyperparameter('decay'),
            'momentum': self.momentum,
            'centered': self.centered,
            'rho': self.rho,
            'centered_v': self.centered_v,
            'epsilon': self.epsilon,
            'belief': self.belief,
        })
        return config
    def _create_slots(self, var_list):
        for var in var_list:
            if self.momentum!=0.0:
                self.add_slot(var, 'm')
            if self.rho!=0.0:
                self.add_slot(var, 'v')

    def _resource_apply_dense(self, grad, var, apply_state=None):
        var_device, var_dtype = var.device, var.dtype.base_dtype
        lr_t = self._decayed_lr(var_dtype)
        local_step = tf.cast(self.iterations+1, var_dtype)

        updates = []

        if self.momentum!=0.0:
            # MomentumSGD
            m = self.get_slot(var, 'm')
            m_t = m.assign( self.momentum*m + (1.0-self.momentum)*grad )
            if self.centered:
                denominator = 1.0-(self.momentum**local_step)
                m_t_hat = m_t / denominator
            else:
                m_t_hat = m_t
            updates.append(m_t)
        else:
            m_t_hat = grad


        if self.rho!=0.0:
            #RMSprop
            if self.belief: # AdaBelief
                g2 = (grad-m_t)**2
                epsilon_b = self.epsilon
            else:
                g2 = grad**2
                epsilon_b = 0.0

            v = self.get_slot(var, 'v')
            v_t = v.assign( self.rho*v + (1.0-self.rho)*g2 )

            if self.centered_v:
                denominator = 1.0-(self.rho**local_step)
                lr_t = lr_t / ((((v_t+epsilon_b)/denominator)**0.5)+self.epsilon)
            else:
                lr_t = lr_t / ((v_t**0.5)+self.epsilon)
            updates.append(v_t)

        # Update var
        var_update = var.assign(var - lr_t*m_t_hat)
        updates.append(var_update)
        return tf.group(*updates)

    def _resource_apply_sparse(self, grad, var, indices):
        raise NotImplementedError("not implemented")

belief=TrueとすればAdaBeliefになる。momentumとrhoの設定は必須。
実装からわかる通り、メモリ消費はAdamと同じ。計算量もほぼ同じと考えてよいだろう。

実験

特性確認

Optimizerを直接実行して、違いをみてみる。具体的な内容は過去記事と同じなので、そちらを参照のこと。

adabelief_curb.png

Bumpy1_MultiOpt (learning_rate=0.02, decay=0.0).gif

赤がAdaBeliefになるが、明らかに他のアルゴリズムとは違う動きになる。最適値に到達するまで最も早い。到達後の振動はAdamより若干大きいが、ほぼ同じくらいのようだ。

画像認識

CIFAR10の画像認識率をAdamとAdaBeliefで比較する。

  • 条件
    • VGGライクの21層のCNN
    • Horizontal Flip,Shift,Cutoutでデータ拡張
    • バッチサイズ512、全200エポックで150エポック以降は学習率を1/10に
    • learning_rate=0.001,momentum=0.9,rho=0.999で実施
    • AdamはTensorFlowの実装を使いパラメータは同じ。
    • Google Colab のTPUで実施

各3回合計6回実施してValidationでのAccuracyをまとめた結果がこちら。

Algorithm Worst Median Best
Adam 94.80 94.88 95.25
AdaBelief 95.45 95.63 95.64

明白にAdaBeliefの結果がAdamを上回った。ネットワークによって性能向上の増減はあるはずなので、興味のある方は自分で確認してもらいたい。

まとめ

軽い気持ちでAdaBeliefを実装してみたら本当に性能が上がっていて驚いた。
WeightDecayやWarmupなどと組み合わせたら、さらに性能向上すると思われる。
Adamを超えるという宣伝文句のアルゴリズムはいくつもあるが、ハイパーパラメータが増えたりして結局使いづらいものが多い。こちらはそのような欠点がないので、Adamにかわる定番になる可能性も十分あるように思われる。

参考

出きたてホヤホヤ!最新オプティマイザー「AdaBelief」を解説!

今度こそAdamを超えるのか⁈NeurIPS 2020で注目された「AdaBelief」とは

関連記事

TensorFlow/Kerasでカスタム最適化アルゴリズムを実装する
最適化アルゴリズムを単独実行で比較する(総合編)
TensorFlow+KerasでCutoutを実装/評価する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む