論文紹介: Disentangling Style and Content in Anime Illustrations

TL;DR

イラスト生成を行う際に、画風(style)と内容(content)に関してdisentangleさせる。

論文:

https://arxiv.org/abs/1905.10742

公開コード:

https://github.com/stormraiser/adversarial-disentangle

既存研究との比較

  • Neural style transfer
    • Image Style Transfer Using Convolutional Neural Networks (CVPR 2016)
      • ランダムノイズの画像を更新していく。この際、CNNの各レイヤーでの中間表現をstyle画像、content画像のそれと比較し、よりよくマッチングするように更新していく。
    • このようなアプローチは中間表現のマッチングに頼っており、そのような事前に用意された特徴量を用いてはドメイン特有なstyleの情報を十分semanticに捕らえられないと指摘。
  • Image-to-Image translation

提案手法

styleのみがラベル付けされていてcontentがラベル付けされていない設定で、それぞれの要素をdisentangleし別々に制御できるような生成モデルを学習する問題設定として扱う。

styleのラベルはアーティストのラベルによって近似する。

  • Generative Adversarial Disentangling Networkを提案
    • 2段階の手法
      • Stage 1: style-independent content encoderの学習
        • style情報ができるだけencodeされないようにしたい。
        • 実験の結果、通常のencoder-decoderの再構成誤差に、潜在表現からのアーティスト予測器の損失が悪くなるような敵対的な項を加えただけでは十分style情報を除くことができなかった。
        • 代わりに、encoderからの潜在表現でアーティストを予測させる代わりに、異なるアーティストstyleで生成させた画像から分類させるように変更
        • f:id:ronwall1701:20210501170022p:plain
          • 画像間の距離はピクセルごとのRGBベクトルのL2距離の平均
      • Stage 2: auxiliary classifier GANsに基づくdual-conditional generatorの学習
        • encoder E, generator G, style function Sはstate 1のもので初期化。
        • Discriminatorの損失
          • f:id:ronwall1701:20210501174609p:plain を用いて
          • \min_D \mathcal{L}_{D-real}+\mathcal{L}_{D-fake}
        • Classifierの損失
          • このclassifierはstage 1のものとは異なる。またclassifierとdiscriminatorには別々のネットワークを用いる。
          • f:id:ronwall1701:20210501175210p:plain
          • f:id:ronwall1701:20210501175255p:plain
            • NLU(y,i)=-\log (1-y_i) はnegative log-unlikelihood
          • これらを用いて \min_{C_2}\mathcal{L}_{C_2-real}+\mathcal{L}_{C_2-fake}
        • Generator, Style encoderの損失

実験

Danbooruにおいて一人のアーティストのラベルしか付いていないイラストから既存の顔抽出ツールにより顔検出。

少なくとも50枚の顔画像を持つアーティストのみを残した。

  • 最終的に106Kの画像と1139人のアーティストがデータセットに含まれる。

f:id:ronwall1701:20210502072200p:plain
生成結果(論文から引用)。一番上の2行はターゲットとしたアーティストのイラスト例。その下は、それぞれの入力イラストに対する提案手法、StarGAN、neural styleによる生成画像。

結果に関するその他コメント

  • content情報を256次元のベクトルに圧縮してしまう提案手法より、解像度が比較的高い潜在表現を保持するStarGANの方が細かいディテールは保持されている。
    • しかし提案手法のようなアプローチはcontentのランダムサンプルが可能な点で強み

COCO Annotatorでアノテーションした自作データセットでSemantic Segmentationモデル(DeepLabv3)を学習する

ふとsemantic segmentationモデルを学習してみたくなったので、自作データセットアノテーションからモデル学習までを既存ツールの組み合わせでやってみました。 備忘録がてら、手順をメモしておきたいと思います。

アノテーション

アノテーションには COCO Annotatorを使いました。GUIが直感的で使いやすく気に入っています。

細かい使い方に関しては解説記事などがわかりやすいと思います。Dockerが使える環境なら非常に簡単にセットアップすることができます。

今回は自作クラスの領域をpolygonツールでアノテーションしました。

アノテーションした後はデータセットのページで「Export COCO」のボタンを押し、対象クラスを選んでエクスポートします。エクスポートされたデータは、上から二段目のタブの「Exports」ボタンを押したページからダウンロードすることができます。

Pascal VOCフォーマットへの変換

COCO AnnotatorはMS COCOフォーマットでデータを出力するため、これをPascal VOCフォーマットに変換するために以下のツールを使います。

github.com

Gitでクローンして必要な環境を整えます。

git clone https://github.com/alicranck/coco2voc.git
pip install pycocotools

次にexample.pyを以下のように修正します。

# L37-39の変数をそれぞれ手元のデータのパスに合わせて修正
annotations_file = "<COCO AnnotatorからダウンロードしたJSONファイル>"
labels_target_folder = "<出力先ディレクトリ名>"
data_folder = "<データセットの画像が含まれるディレクトリ名>"

# その下に以下を追記
import sys
sys.path.append(data_folder)

さらに、coco2voc関数が呼び出されている箇所の引数のうちnの部分をn=Noneのように修正します。

その後、python example.pyを実行すると出力先ディレクトリに指定した場所にclass_labels/が生成されます。このディレクトリ以下の画像を学習に利用することになります。

DeepLabv3の学習

今回は以下のPyTorch実装を利用します。

github.com

環境整備

まずGitからクローンを行い、環境を整備します。

git clone https://github.com/VainF/DeepLabV3Plus-Pytorch.git

cd DeepLabV3Plus-Pytorch
pip install -r requirements.txt

自作データセット読み込みクラスの作成

続いて自作データセットの読み込みクラスを作成します。

今回はとにかくモデルを学習することを優先し、やり方はスマートではないですが、既存データセットのDatasetクラスを自作クラスのものに置き換えてしまう方針を取ります。

そのために、datasets/mydata.pyの名前でファイルを作成し、以下のように記述します。手元のデータに合わせて修正すべき箇所は次の2点です。

  • クラスコンストラクタ冒頭のorig_json, mask_dir, image_dirの3変数。
    • それぞれCOCOフォーマットでのJSONファイル、coco2vocの出力先ディレクトリ以下にあるclass_labelsのパス、画像が保存されているディレクトリパスに修正します。
  • 最後のdecode_target関数
    • cmapは2次元配列で、各行が各クラスを表示する際の色となっています。クラス数に合わせて変更してください
import json
import os

import numpy as np
from PIL import Image
import torch.utils.data as data

class MyDataSegmentation(data.Dataset):
    def __init__(self, root,
                 year='2012',
                 image_set='train',
                 download=False,
                 transform=None):
        # 適宜必要に応じてimage_setがtrainの時とvalの時で分けることもできます
        orig_json = "path/to/json/file/of/dataset"
        mask_dir = "path/to/output/dir/class_labels"
        image_dir = "path/to/image/directory"

        self.images = []
        self.masks = []

        with open(orig_json) as h:
            coco_data = json.load(h)
        for img_data in coco_data["images"]:
            self.images.append(
                os.path.join(image_dir, os.path.basename(img_data["path"]))
            )
            self.masks.append(
                os.path.join(mask_dir, f"{img_data['id']}.png")
            )
        
        self.transform = transform


    def __getitem__(self, index):
        img = Image.open(self.images[index]).convert('RGB')
        target = Image.open(self.masks[index])
        if self.transform is not None:
            img, target = self.transform(img, target)

        return img, target
    
    def __len__(self):
        return len(self.images)
    
    @classmethod
    def decode_target(cls, mask):
        cmap = np.array([
            [0, 0, 0],
            [255, 0, 0]
        ])
        return cmap[mask]

続いて、datasets/__init__.pyを以下のように修正します。

#from .voc import VOCSegmentation
from .mydata import MyDataSegmentation as VOCSegmentation
from .cityscapes import Cityscapes

学習

--model引数により学習するモデルアーキテクチャを変更できるようです。詳細はレポジトリのREADMEをご参照ください。

python main.py --model deeplabv3plus_mobilenet  --gpu_id 0 --year 2012_aug --crop_val --lr 0.01 --crop_size 513 --batch_size 16 --output_stride 16

評価

resultsディレクトリ以下に画像で結果が出力されます。

python main.py --model deeplabv3plus_mobilenet --gpu_id 0 --year 2012_aug --crop_val --lr 0.01 --crop_size 513 --batch_size 16 --output_stride 16 --ckpt checkpoints/ --test_only --save_val_results

アニメ系イラストに対する機械学習研究や実装をまとめてみた

未来の自分のためにだらだらと見かけたものをメモしていこうと思います。

研究まとめ系

タグ分類

キャラクター分類

物体検知

Semantic Segmentation

ランドマーク検出

ポーズ推定

高解像度化

Inpainting

創作支援系

イラスト生成

  • youyuge34/PI-REC

    • 手描きの線画と色ヒントを入力に生成する
  • diva-eng/stylegan-waifu-generator

    • StyleGANによるアニメキャラの顔画像生成
    • 追記:事前学習モデルをまわすにはrosinality/stylegan2-pytorchをcloneしてパスを通す必要がありました。
    • 以下は実際にこの事前学習モデルによって生成されたイラストです
      • f:id:ronwall1701:20210409165115p:plain

カラー化 (Colorization)

  • DwangoMediaVillage/Comicolorization
    • 白黒の漫画に自動的に色を付ける Comicolorization を試せる実装。
    • パネル検知なども行っている。
    • ユーザーが与えたキャラクターの色付け例を参考に色を付けてくれる。
    • 以下の図は現論文からの引用。一番左がユーザーが与える色付け例の参考画像。
      • f:id:ronwall1701:20210328053221p:plain

線画着色 (Lineart Colorization)

ライティングの変更

スタイル変換

線画の補正

白黒漫画化

実写のトゥーン化

実写の写真を入力として、絵画調に変換する。

データセット

アニメキャラ識別データセットを作った話


TL;DR

  • アニメキャラ識別のデータセットを作った
  • 人間のエラー率13%に対し、ベースラインモデルは70%程度のエラー率にとどまっており改善の余地は大きい

データセット作成の動機

以前metric learningで人間並みのアニメキャラ識別性能を目指す!の記事でアニメキャラ識別に関する取り組みの結果を紹介しましたが、やはり一人の力では性能改善に限界があると感じました。 そこで、いろいろな人が同じデータセットでアニメキャラ識別の性能改善に取り組めるようにベンチマークデータセットを作成することにしました。

ZACI-20データセット

作成したベンチマークデータセットZACI-20については以下のレポジトリで公開しています。

github.com

このデータセットDanbooru2020データセットに含まれるイラストに対して顔検知を行い、以下の基準を満たしたイラストから得られた顔画像を対応するキャラクターラベルでアノテーションしたものです。

  • キャラクタータグが1つしか付けられていない
  • 顔検知の結果、顔が1つしか検出されなかった

データセットには約3万9000人のキャラクターにわたる、合計約145万枚の顔画像が含まれています。

zero-shotキャラクター識別タスク

このデータセットの応用先として、zero-shotキャラクター識別タスクを主に想定しています。

zero-shotキャラクター識別タスクとは、訓練時に未知なキャラクターの顔画像が2枚与えられた際に、それらの画像に描かれているものが同じキャラクターかどうかを推定するタスクです。(名付けが適切かどうかはわかりませんが。。。) このタスクにおける性能を評価するため、テストデータには同じキャラクターの顔画像のペア(正例)と、異なるキャラクターの顔画像のペア(負例)が含まれています。

タスクの難易度を上げるため、負例ペアは訓練データで学習したモデルがより間違えやすいようなペアを敵対的に選んでいます。 以下の図はそのようにして得られた負例ペアの具体例を示していますが、髪の色が似ているキャラクターのペアなど、区別が難しいペアが選択されていることがわかります。

https://github.com/kosuke1701/ZACI-20-dataset/raw/main/misc/adversarial.png

ベンチマーク

このタスクにおいて現在の深層学習モデルがどの程度人間の性能に迫っているのかを評価するため、テストデータに対して人手でアノテーションを行うことで人間の識別性能を評価しました。 比較対象の深層学習モデルとして、前回の記事と同様にmetric learningを用いて学習したResNetモデルと、arkel32/animesionにおいて公開されていたコードにより学習したVision Transformerモデルを評価しています。

この図から明らかなように、このタスクにおいては(少なくとも私が学習した)深層学習モデルは人間が13.6%のエラー率で識別できるテストデータに対し70%前後のエラー率しか達成することができていません。

f:id:ronwall1701:20210316182414p:plain

おわりに

この記事ではアニメキャラ識別の性能改善のために作成したデータセットZACI-20について紹介しました。 このデータセットが、今後のアニメキャラ識別モデルの性能改善に貢献できれば嬉しいです。

metric learningで人間並みのアニメキャラ識別性能を目指す!

はじめに


今回はmetric learningと呼ばれる技術を応用して、同じアニメキャラが写っている顔画像ほど類似度が大きくなるような特徴量空間を学習したいと思います。

この記事の内容は以前Qiitaで投稿した記事と同じプロジェクトをより発展させた(と思っている)ものです。

結論だけ知りたい人へ

人間のエラー率1.2%に対し、深層学習を使った今回の取り組みではエラー率11.3%と人間にはまだまだ及ばない結果になってしまいました。今後引き続きいろいろ取り組みを行って続編ではもう少し精度を上げられればと思います。

デモ

今回の取り組みで一番性能が良かったモデルを試せるデモを用意しました: Open In Colab

画像を2枚アップロードして、そこから抽出されたキャラクターの顔の間の類似度スコアを計算して表示するようなシンプルなデモになっております。

技術的な話


metric learning

Metric learningとは、似ている入力データ同士の距離が小さくなる(類似度が大きくなる)ような特徴量空間への変換を学習する技術です。

たとえばデータ  \boldsymbol{x} と、それぞれ似ている・似ていないデータ  \boldsymbol{x}^{+}, \boldsymbol{x}^{-} があったとき、何らかの距離指標  dist(\cdot, \cdot) において  dist(f(\boldsymbol{x}), f(\boldsymbol{x}^{+})) \lt dist(f(\boldsymbol{x}),f(\boldsymbol{x}^{-})) となるような変換  f(\cdot) を学習することが目的です。

既にわかりやすい解説記事が多くありますので、詳細な説明は例えば以下の記事に譲りたいと思います。

qiita.com

今回はこのmetric learningの技術を応用するに当たり、既に様々なmetric learningの損失関数がPyTorchで実装されているpytorch-metric-learningライブラリを利用させていただきました。

github.com

ハイパーパラメータ探索

これまでmetric learningの損失関数は様々なものが提案されており、性能が改善されていっている事が報告されていました。しかし以下の記事で紹介されている論文で示されているように、より公平な比較を行ったところ既存の損失関数はこれまで報告されていたほどには性能を改善していないのではないかという意見も出されています。

blog.seishin55.com

そこで今回の取り組みでは単に最新の損失関数を使うのではなく、Contrastive損失などの古典的な損失関数も含めて評価の対象とすることとしました。

前述した論文でも示されている通り、ハイパーパラメータのチューニングは手法間の公平な比較には重要です。 そこで、今回は勉強も兼ねてハイパーパラメータチューニングのライブラリであるoptunaを使ってみました。

github.com

実験


利用データ

モデルの学習には、個人的に収集した11万枚のアニメキャラ顔画像データを利用しました。

顔部分の切り出しには、以前Qiitaの方で紹介したEfficientDetを自作データセットで学習したモデルを利用しています。詳細は以下の記事やレポジトリをご参照ください。

qiita.com

github.com

これらの顔画像にはタグに基づくキャラクターのラベルが付けられています。 キャラクターの総数は3502で、1キャラクターあたりの平均顔画像数は31です。 各キャラクターは少なくとも3枚以上の顔画像を持ちますが、低頻出キャラクターの数が多く頻度が3,4のキャラクターがそれぞれ720, 508もいます。

一方テストデータには、2枚の顔画像しか持たない1399人のキャラクターの顔画像を利用します。

このテストデータにおいて同じキャラクターの顔画像ペア全てを正例とし、ほぼ同じ数の異なるキャラクターの顔画像ペアを負例としてランダムにサンプルしました。

テスト時には、上記画像ペアが同じキャラクターかどうかの分類を正しく行えるかどうかを評価します。

実験コード

以降で説明するハイパーパラメータチューニングに関する実験コードは以下のGithubレポジトリで公開しています。ドキュメントが適当なので使い方など分かりづらいかもしれませんが、興味がある方はご覧ください。

各クラスの画像がフォルダごとにまとめられている形式のデータセットであれば、設定ファイルをいじるだけでそのまま利用できるはずです。

github.com

結果


人間

まず人間である私がテストデータに対してアノテーションを行った結果、FPR(False Positive Rate)=1.22%, FNR(False Negative Rate)=22.0%という結果になりました。

FNRがやや大きく感じますが、同じキャラクターであっても絵柄が大きく異なったりすると同じキャラクターであると判断するのは案外難しいです。 またラベルノイズや顔検出の失敗も含まれるため、こんなものだろうという感覚です。

今回はこの人間の性能を目指し、同じFNRでどれだけFPRを人間と同じくらい小さくできるかを調べていきたいと思います。

ベースライン

深層学習モデルの性能を評価する前に、基準となるシンプルな方法での性能を調べてみたいと思います。

1つ目のベースラインはランダムに答えを出力するものです。

2つ目のベースラインは、LAB色空間における色ヒストグラムのコサイン類似度を用いてキャラクターが似ているかどうかを判断するものです。画像ごとのヒストグラムの計算方法の実装は以下のとおりです。nbinについては性能が最も良かったnbin=5に設定しました。

import cv2
import numpy as np

def color_histogram(path, nbin):
    img = cv2.imread(path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    hist = cv2.calcHist(img, [0,1,2], None, [nbin]*3, [0,256, 0,256,0,256])
    vec = hist.flatten()
    vec = vec / np.sum(vec)
    return vec

これらのベースラインの結果は以下のとおりになりました。

ヒストグラムを使ってもランダムより多少良い程度で、人間の識別能力には全く及ばないことがわかります。

手法 FPR
ランダム 77.9%
ヒストグラム 68.5%
人間 1.22%

損失関数の比較

続いて様々に提案されているmetric learningの損失を用いて学習した深層学習モデルの性能を比較してみます。

深層学習モデルにはResNet-18でクラス分類を行う全結合層を、500次元の特徴量ベクトルに変換する全結合層に置き換えたモデルを利用します。また、この全結合層の後にDropoutレイヤーを加え、特徴量ベクトルはL2ノルムが1となるように正規化されます。

この深層学習モデルを学習するにあたって、今回は以下の4種の損失関数を比較しました。

  • Contrastive
  • Triplet
  • ArcFace
  • ProxyNCA

それぞれの損失関数の性能をできるだけ正確に比較するため、以下のページに公開されている既存研究のハイパーパラメータ探索領域も参考にしながらハイパーパラメータ探索を行いました。

kevinmusgrave.github.io

損失関数のハイパーパラメータ探索範囲はそれぞれ以下のとおりとしました。

  • Contrastive
    • pos_margin: [0,2]
    • neg_margin: [0,2]
  • Triplet
    • margin: [0,2]
  • ArcFace
    • margin: [0,90]
    • scale: [0.01,100]
  • ProxyNCA
    • scale: [0,100]

またembeddingに対しL1,L2正則化を行うかどうかもチューニングし、行う場合は係数を10^{-6}から 10^{2} までの範囲で探索しました。 モデル(および一部の損失関数)の最適化にはRAdamを利用し、学習率は10^{-5}から10^{-2}の間で、weight decayの係数は10^{-10}から10^{-2}の間で探索しました。

探索に利用できる計算資源には限りがあるため、optunaによる最大試行回数は各損失関数あたり60回とし、エポック数も最大30として2エポック連続で性能(MAP@R)が改善しなかった場合は学習を打ち切るearly stoppingも行います。

またハイパーパラメータ探索中は、データのばらつきのよる評価のゆらぎを抑えるため、2つの異なるtrain-devセットの分割における性能を平均しました。 なおテスト時にはより多くのデータを使ってモデルが収束するまで学習するため、early stoppingの条件を緩くするなど探索時とはやや条件を変えて再学習しています。

それぞれの損失関数で最良の結果は以下のとおりとなりました。 先程までのベースラインと比べて、大きくFPRが改善していることがわかります。

一方で、人間の識別能力にはまだまだ及ばない結果となってしまいました。

損失関数 探索時のMAP@R (\uparrow ) 再学習時のMAP@R (\uparrow ) 再学習時のFPR (\downarrow ) 再学習時のEER (\downarrow )
Contrastive 0.260 0.308 12.2% 15.0%
Triplet 0.248 0.272 13.0% 16.0%
ArcFace 0.213 0.241 14.8% 17.7%
ProxyNCA 0.236 0.251 16.6% 18.9%
(参考)人間 N/A N/A 1.22% N/A

また損失関数に注目すると、興味深いことに最も古典的なContrastive損失が最も良い性能を示しました。 60回の試行回数で十分だったのかという疑問もあるため断定はできませんが、少なくとも今回の探索方針・データセット・タスクでは他の損失関数よりもContrastive損失が有用であったということのようです。 metric learningの損失に関しては、とりあえず新しい損失を使っておけば良いわけではないということが言えるのではないかと思います。

なお以後の実験も含め、設定ごとの探索結果の詳細はGoogle スプレッドシートにまとめてあります。興味のある方はご覧ください。

少し脇道にそれますが、得られたハイパーパラメータの探索結果を上記既存研究で得られていたものと比較してみます。

今回Contrastive損失に対しては

  • pos_margin: 0.681
  • neg_margin: 1.04

というハイパーパラメータが最良の結果を示しました。一方既存研究ではCUB200データセットに対するContrastive損失のハイパーパラメータ探索結果として以下のような結果が得られています。

f:id:ronwall1701:20201226075636p:plain
https://kevinmusgrave.github.io/powerful-benchmarker/papers/mlrc_plots/cub_contrastive.html より引用

データセットやその他探索の条件などが異なるため一概に比較はできませんが、それぞれのデータセットで性能が最適なハイパーパラメータの領域は当たらずとも遠からず、といった印象を受けます。

新しいデータセットでmetric learningのハイパーパラメータを探索する際は、既存研究での探索の結果良さそうなハイパーパラメータを初期情報として与えることも有用なのかもしれません。

次元数・データ拡張(Data augmentation)の効果

これまでの実験では最終的なembeddingの次元を500に固定し、精度向上のためのデータ拡張は行っていませんでした。この節ではこれらの設定を変えることでより性能が向上できないかを調べます。

次元数

まず次元数を増やしたときの効果について調べます。

損失関数は前の節の結果を踏まえてContrastive損失を用いることとします。また次元数を増やすことによって最適なdropoutや距離に対するマージン閾値が変わってしまう可能性があるため、前の節と同様のハイパーパラメータ探索を行いました。

結果は以下の通りです。次元数を500から1000に増やしても、性能を大きく改善させることはできませんでした。そこで以降の実験では次元数は500のままとすることにします。

次元数 探索時のMAP@R (\uparrow ) 再学習時のMAP@R (\uparrow ) 再学習時のFPR (\downarrow ) 再学習時のEER (\downarrow )
500 0.260 0.308 12.2% 15.0%
1000 0.264 0.296 11.9% 16.1%

データ拡張(Data augmentation)

続いて、入力画像をランダムに編集してデータを水増しするデータ拡張を用いた場合の効果について調べていきたいと思います。

古典的なデータ拡張

まずは数年前から用いられているような、古典的なデータ拡張の手法を試してみたいと思います。利用するデータ拡張の種類は、以下の論文で有効性が示されていたFlip, Rotation, Translation (Shift), Erasingの4種類とします。

search.ieice.org

RotationとTranslation, Scaleについては、データ拡張の強度を以下のハイパーパラメータで調整します。

  • Rotation
    • 回転角: [0, 180]
  • Translation
    • 画像サイズに対する平行移動の大きさ: [0,0.3]
  • Scale
    • 拡大・縮小率: [1,1.3]

これらのハイパーパラメータおよび、Flip, Erasingを適用するかどうかを再びoptunaによりチューニングします。損失関数はContrastive損失を用い、次元数は500とします。そして探索空間を削減するため、損失関数の比較の際にチューニングしておいたハイパーパラメータは最適値で固定しました。

RandAugment

また、より発展的な手法として、データ拡張の手法として近年提案されたRandAugmentも試してみたいと思います。 この手法は、少ないハイパーパラメータの探索空間で対象の問題に良いデータ拡張を実現することを目指しています。 探索すべきハイパーパラメータは、適用するデータ拡張の手法数  N と、データ拡張の強度  M の2つのみです。

論文や解説記事、利用した実装を以下に紹介しておきますので、興味のある方はご覧ください。

論文:

https://arxiv.org/pdf/1909.13719.pdf

実装:

github.com

解説記事:

qiita.com

このケースでは探索空間が小さいため、以下のハイパーパラメータ設定に対してグリッドサーチを行いました。

  •  N: [1,2]
    • N=0の場合はデータ拡張なしに相当するため探索しませんでした。
  •  M: [2,6,10,14,18,22,26,30]

結果

結果は以下のとおりです。データ拡張を行うことによって性能をやや向上できました。

またRandAugmentがFlip, Rotation, Translation, Erasingの4種を組み合わせてチューニングした結果よりも良い性能を達成していることも確認できます。今回RandAugmentはグリッドサーチで18パターンの探索のみでこの性能を得ることができているため、非常に使い勝手も良い手法だと感じました。

データ拡張 探索時のMAP@R (\uparrow ) 再学習時のMAP@R (\uparrow ) 再学習時のFPR (\downarrow ) 再学習時のEER (\downarrow )
なし 0.260 0.308 12.2% 15.0%
古典的なデータ拡張 0.284 0.315 11.6% 15.2%
RandAugment 0.279 0.341 11.3% 14.3%

まとめと今後の課題

以上、しっかりハイパーパラメータチューニングを行った上でいろいろな損失関数やデータ拡張の方法を試してみましたが、まだまだエラー率は11%と、人間の識別能力(エラー率1.2%)には大きく水をあけられてしまっています。

今後はResNet-18よりもより大きなモデルの利用や、教師なし学習による事前学習の利用、そして訓練データ数を増やすなどの方策によってどの程度性能が改善できるかを調べて行けたらと思います。(やる気が続けばですが…)

論文紹介: Sketch Generation with Drawing Process Guided by Vector Flow and Grayscale

紹介論文

最近以下の論文がArxivに投稿されました[1]。(AAAI 2021にも採択されているようです。) 画像を基に鉛筆画を生成する技術ということですが、個人的に興味がある分野なので読んでみました。またこの記事中の図は特記しない限り原論文[1]から引用しています。

arxiv.org

公開実装

著者らにより実装が公開されています。

github.com

ただ手元の画像ですぐに試すには少し不便だったので、Google colaboratoryで簡単なノートブックを作成しました。

手元の画像でCPUインスタンスで実行すると完了までに数時間かかったので、場合によっては小さい画像で試したほうが良いかもしれません。

colab.research.google.com

論文内容

目的と新規性

この論文で取り扱うタスクの目的は、入力画像を鉛筆画に変換することです。

3DモデルとNon-Photorealistic Renderingを用いるアプローチなど、このタスクに対して従来から様々な手法が提案されています。 しかしこれらの従来手法は最終的な鉛筆画のみを出力し、鉛筆画を描いていくプロセスを出力することはできませんでした。

この研究の新規性は最終的な出力のみでなく、以下の図のように一本一本線を引いていくプロセスも出力できることです。

f:id:ronwall1701:20201222182537p:plain

手法

提案手法は鉛筆画とその描画のプロセスを出力するための問題を以下の2つに分解して解いています。

  • 鉛筆の線のシミュレーション (stroke simulation)
  • どのような鉛筆の線を引くかの決定 (stroke drawing)

Stroke simulation

まず最初のstroke simulationでは、実際の鉛筆画の線に近い線を再現することが目的になります。 この目的を達成するため、この研究では実際の鉛筆画で線がどのように引かれているかを分析しています。

線を水平にする

実際の鉛筆画はある一部分のみに注目すると、平行な線によって構成されていることがわかります。 そこで、処理の最初の段階でこのような平行な線が並んでいる部分を切り出し、線が水平になるように回転させます。

線の明度分布を得る

次に、線に垂直な方向(以下の図(a)におけるyの矢印)に並ぶ各ピクセルの明度がどのように変化するかを調べます。 以下の図の例では、この明度の変化の例が(b)に示されています。 この図をみるとわかるように、それぞれの鉛筆の線は明度の変化のグラフ上でV字型のパターンとして現れます。

この特徴を利用すると、グラフ上の明度の極大値の位置で区切る((b)における赤の点線)ことにより、個々の線ごとの「V字型」、すなわち鉛筆の線を垂直に切ったときの明度変化のパターンを取り出すことができます。 さらにこの処理を図(a)のピクセル列(x座標が同じピクセル)それぞれに対して実行することで、それぞれの線毎に複数の「V字型」パターンを得ることができます。

この論文では、この「V字型」に関して以下の仮定をしています

  • 同じ線に注目したとき、線の中心(最も明度が低いピクセル)からの距離が同じピクセルの明度はi.i.d.にサンプルされる
    • この分布をガウス分布と仮定し、平均と分散を計算する
  • 上記平均と分散は、線の中心からの距離 d、中心明度 G 、線の幅  W により以下のようにモデル化される
    •  mean(d)=G+(255-G)\times \frac{2d}{W-1}
    •  variance(d)=(255-G)\times cos \frac{\pi d}{W-1}

f:id:ronwall1701:20201222182826p:plain

Stroke drawing

以上のstroke simulationの節で説明した処理によって、鉛筆画の線を統計的にモデル化することができました。

よって画像を鉛筆画に変換するためには、どのような濃さ、幅、長さの線を、どこに、どの方向に向かって描くかを決めればよいです。

方向の決定

線を描く方向は、入力画像の勾配情報を用いて決定します。

より具体的には、Edge Tangent Flow (ETF) [2]と呼ばれる手法を利用したそうです。勾配に対して垂直な方向が線を引く方向になります。

最後にピクセルごとに得られた方向を予め決められた粒度に量子化し、それぞれの領域の中では平行な線が引かれるような領域に分割してやります。

線を引く

線を引く処理は、上の処理で作成された平行な線が引かれる領域ごとに行われます。

まず最初に各ピクセルの明度を量子化します。この量子化された明度が線の中心の濃さになります。

そしてこの濃さに基づいて、描画対象の領域に予め与えられた線の間の幅(+少しのノイズ)だけ離れた平行線を引いていきます。

  • なお、ここでstroke simulationで導入したモデルにより、線の幅と中心の濃さが決まれば線の幅方向の明度の分布を決めるていると思われます。

複数領域の線の合成

以上の処理で領域ごとに平行線を引いていくことができました。

しかしこれを単に合成すると以下の図の左側のように、領域の境界部分でうまく線がつながらなかったりする現象が起きてしまいます。

この問題を解消するため、後処理として境界部分の線を線の幅だけ伸ばしてやる処理を行います。この後処理を行った例が以下の図の右側です。 ただしこの図からもわかるとおり、この後処理によりぼやけていまう箇所がでてきてしまいます。 そこで、エッジを強調するため、edge mapを最後に乗算する処理も行います。

f:id:ronwall1701:20201223185443p:plain

線の描画順の決定

これまでの処理で鉛筆画を構成する線のリストが得られたため、後はこれらの線をどのような順番で描いていくかを決めればよいです。

実際の画家が鉛筆画を描くときにはくっきりとした物体の輪郭に最初に線を引く、という観察に基づき、この論文では以下のように線の明度 Gと線に対応する領域 D内のピクセルにおける勾配の大きさ T_iに基づくスコアを計算して大きい順に線を引いていきます。

 S=(255-G) \times \sum_{i\in D}T_i

実際に使ってみた

最初の方で紹介したcolabノートブックを使って、実際に写真を鉛筆画に変換してみました。以下の写真は論文から引用したものではなく私が用意したものになります。

入力画像はこちらのうな重の画像です。

f:id:ronwall1701:20201223194223j:plain

そして最終的な出力はこちらのようになりました。

f:id:ronwall1701:20201223194452j:plain

またこの手法の特徴として、以下のように鉛筆画生成の途中結果も生成することができます。

f:id:ronwall1701:20201223194903j:plain

うな重のお盆に筋状の模様が入っているせいか、そこに引きずられてスプーンやおさらの輪郭より前にお盆のディテールを描いてしまっているところが目に付きますね。 Semantic segmentationとかの出力を使って上手くこの辺を改善できないかが気になります。

感想

論文を見かけたときにはもう少し機械学習要素が強い手法かと思っていましたが、実際に読んでみるとかなり地道な分析や処理を積み上げているようで少し意外でした。

写真から手書き風の背景を作成するなど、創作向けの応用で使えないかな〜といろいろ考えています。

最後までお読み頂きありがとうございました。なにか間違えているところなどあればコメントなどでご連絡ください!それでは!

参考文献

[1] Tong, Zhengyan, et al. "Sketch Generation with Drawing Process Guided by Vector Flow and Grayscale." arXiv preprint arXiv:2012.09004 (2020).

[2] Kang, Henry, Seungyong Lee, and Charles K. Chui. "Coherent line drawing." Proceedings of the 5th international symposium on Non-photorealistic animation and rendering. 2007.

学習済みspeaker embeddingの性能比較をしてみた

はじめに

皆さんは声優の声を分類したり比較したりしてみたいと思ったことはありませんか? 関連する技術として、話者認証(speaker verification)などの分野で、人の声が似ているかどうかを判断するためのベクトル表現としてspeaker embeddingを計算するモデルがいろいろと研究されているようです。

しかし一からモデルを学習するのはデータも計算リソースも時間も必要なので大変です。 そこで、今回は事前学習済みモデルを公開してくださっている研究を探して、実際に日本語のデータで声優の声を区別する性能を比較してみることにしました。

実験コードは公開しています。この記事に含まれていない事前学習済みモデルなどをご存じの場合はコメントなどで教えていただければ幸いです!

学習済みspeaker embeddingモデルの比較実験

比較方法

比較実験には公開されている事前学習済みモデルをそのまま利用しました。特にfine-tuningなどは行っていません。

データ

テストデータには、100人の声優が日本語テキストを読み上げた音声データを収録しているJVS (Japanese versatile speech)コーパスを利用しました。

評価指標

得られたspeaker embeddingを用いて与えられた音声ペアが同じ声優の音声かどうかを分類し、性能を比較しました。

具体的には、以下のようなpositive, negative音声ペアを各クラス10000個ずつランダムに作成し、ペアごとの類似度を分類に用いるスコアとして利用しました。(当然各比較実験で利用されるペアは手法が違っても同一としました。)

  • positive
    • 話者が同じ2つの音声のペア
  • negative
    • 話者が異なる2つの音声のペア

以下の結果ではこのスコアの評価指標としてEERを示しています。

話者認証の研究では複数のenroll voiceを利用した認証の性能をEERで示しているようですが、今回は実装の簡便さのために上記のような方法を取っています。

実装コード

評価の際に利用したGoogle colaboratoryのコードを公開しています。

JVSコーパスのデータセットのzipファイルをGoogle driveresearch/jvs_ver1.zipとして配置してある前提です。(Google driveのマウントが必要です。)

結果

評価の結果は以下のようになりました。

juanmc2005/SpeakerEmbeddingLossComparisonにおいて公開されている事前学習済みのモデルが最も性能が良い結果になっています。

実装 EER Google colaboratory 動作例 Note 
Jungjee/RawNet [Jung+20] 23.5% サンプル 固定長への切り取りをランダムに15回行い平均。
CorentinJ/Real-Time-Voice-Cloning [Jia+18] 20.6% サンプル
philipperemy/deep-speaker [Li+17] 18.0% サンプル
juanmc2005/SpeakerEmbeddingLossComparison [Coria+20] 16.8% サンプル 論文によると、SincNet [Ravanelli+18] による特徴量抽出とx-vector [Snyder+18]をend-to-endにadditive angular margin lossで学習しています。

おわりに

今回は事前学習済みモデルが公開されているspeaker embeddingモデルを比較してみました。 今後はこれらのモデルを利用していろいろと遊んでいければと思っています。

参考文献

  • [Coria+20]
    • Coria, Juan M., et al. "A Comparison of Metric Learning Loss Functions for End-To-End Speaker Verification." arXiv preprint arXiv:2003.14021 (2020).
  • [Jia+18]
    • Jia, Ye, et al. "Transfer learning from speaker verification to multispeaker text-to-speech synthesis." Advances in neural information processing systems 31 (2018): 4480-4490.
  • [Jung+20]
    • Jung, Jee-weon, et al. "Improved RawNet with Feature Map Scaling for Text-independent Speaker Verification using Raw Waveforms." Proc. Interspeech 2020 (2020): 1496-1500.
  • [Li+17]
    • Li, Chao, et al. "Deep Speaker: an End-to-End Neural Speaker Embedding System." arXiv (2017): arXiv-1705.
  • [Ravanelli+18]
    • Mirco Ravanelli and Yoshua Bengio. Speaker Recognition from Raw Waveform with SincNet. 2018 IEEE Spoken Language Technology Workshop (SLT), pages 1021–1028,
  • .
  • [Snyder+18]
    • David Snyder, Daniel Garcia-Romero, Gregory Sell, Daniel Povey, and Sanjeev Khudanpur. X-Vectors: Robust DNN Embeddings for Speaker Recognition. 2018 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), pages 5329–5333, 2018.