LoginSignup
0
1

[論文読み]GANのサーベイ

Last updated at Posted at 2024-05-25

こんにちは。ぺいぺいです。この度、研究でGANについて触れることがあったので、survey論文を読み内容をまとめておくことにしました。間違いなどがありましたら指摘いただければと思います。対象の論文は、"A survey on GANs for computer vision: Recent research, analysis and taxonomy" であり、GANの基本的な考え方や、問題点、派生アーキテクチャなどに触れていきます。

Introduction

GAN(Generative Adversarial Networks)は2014年に提案された手法である。その目的は、データ分布をまねて、データを生成することである。GANの登場により、データ生成において革新的な進歩が生じた。例えば、以下の画像はGANで生成されたアイドルの画像である。

image.png
引用元:https://hacker.hatenadiary.com/entry/2018/12/15/201501

GANの主な特徴は、2つのニューラルネットワーク(NN)が競い合い、min-max gameをすることである。min-max gameは特にゲーム理論の文脈で使われる概念で、二人以上のプレイヤーがいるゲームにおいて、各プレイヤーが自分の損失を最小限に抑えるために最適な戦略を選択しようとする状況を指す。2つのNNは互いの相反する目的関数を最適化しなければならない。
image.png

このような2つのNNを使用する複雑性が原因となり、モード崩壊などの通常の学習では生じないような困難な点が生じ、それらを解決するために新たなモデルが提案されてきた。

GANの構造や問題点について

GANの定義と構造

GANはさまざまなNNで構成されることがあるが、その目的はデータ構造を教師なし学習により真似ることである。この目的を達成するために、2つのNNが用いられる。1つはGenerator(G)もう一つがDescriminator(D)である。Gはデータをコピーせずに生成する役割を担い、Dは実データと生成データを識別する役割を持つ。

フォーマルに表現すると、Dは$p(y|x)$を推定する。これは、サンプル$x$が与えられた際のラベル$y$の確率分布である。具体的例として、$x$は生成画像もしくは実画像、$y$は実画像なら1生成画像なら0となるラベルが挙げられる。一方、Gは潜在空間$z$からデータを生成する役割を持つため、G($z$)と表現される。潜在空間とは、データの情報が反映されたベクトル空間を指す。推論時の実用上は$z$はランダムなベクトルとしてseedのように扱われ、G($z$)により画像が生成されたりする。

このGとDを競わせることによって、互いの精度を向上させていく。Dは実データと生成データの識別精度が最大化するように学習し、Gは最小化するように学習する。この損失関数は以下のように表される。
$$
\text{min}_{G} \text{max}_D L(D,G) = E_xlog[D(x)] + E_z log[1-D(G(z))]
$$

ここで、$x$は分布$p(r)$に従う実データであり、$z$はガウスノイズや一様ノイズの分布$p_z$に従う潜在変数である。最大化されたDのもと上記の目的関数を最小化するGを選ぼうとすると、実分布$p(r)$とGにより生成する分布のJSダイバージェンスを近づけるGが得られる。すなわち、実分布を真似ることが可能。
https://qiita.com/typecprint/items/035478748a811dd29721

主な問題点

GANはその特性上、普通の学習ではないような問題点が生じることがある。

モード崩壊(Mode collapse)

モード崩壊は、異なる潜在空間からの入力$z$に対して、似たようなデータを生成してしまう減少である。以下の画像はモード崩壊の例である。異なる入力値に対して似たような手書き文字を生成してしまっている。
image.png

どんな入力に対しても全く同じ出力をするcomplerte mode collapseといった現象もまれに発生するらしいが、大部分は部分的なモード崩壊であるpartial mode collapseである。

勾配消失(Gradient vanishing)

GANはその学習の特性上、GとDのバランスが重要になる。これらのバランスが崩れると、勾配消失が発生し、うまく学習が進まなくなる。

識別器Dが非常に正確な場合:

Dが非常に高い性能を持つと、本物のデータ(D(x)=1)と生成されたデータ(D(G(z))=0)を完璧に区別できるようになる。このとき、Dはほとんどのケースで正確なラベルを出力し、誤差が非常に小さくなる。
誤差が小さいと、バックプロパゲーションによって生成器Gに戻される勾配も非常に小さくなる。これが勾配消失を引き起こし、Gが有効なフィードバックを受け取れず、改善が難しくなる。

識別器Dの精度が低い場合:

Dが不正確で、本物と生成データの区別がつかない場合、Dからのフィードバック(学習シグナル)が無意味になる。つまり、Gがどのようにデータを生成してもDは正確な評価を行えないため、Gは正しい方向に向かって学習が進められない。

Instability

GANでは、GとDが協力して、グローバルな損失関数を最適化するが、DとGの最適化する式が正反対である。そのため、学習時において、一方のNNの変化が他方の大きな変化につながってしまうことがある。GANにおいてDとGの精度が均衡している際、「同期している」と表現されるが、大きな勾配によるNNの大幅な更新は同期を崩しうるため、学習がデリケートになる。

学習の不安定さはさらなる不安定さを生み出す可能性があり、そうなるとこれらの問題は長引く傾向にある。NNはこの不安定なプロセスを自己修復する可能性も持っているが、そうなったとしてもモデルのパフォーマンスは落ちてしまう。近年の研究では、これらの問題点を解決し、安定した学習を目指すものがある。

Stopping problem

通常のNNと異なり、損失関数の推移に決まったパターンがない。そのため、いつ学習を打ち切れば良いのかの判断が難しい。これをStopping problemと呼んでいる。
image.png

評価関数

GANの性能を1つで全て測ることのできる便利な評価指標は今のところ存在していない。というのも実データ分布を真似るというタスクの特性上、さまざまな応用ができ、応用先のタスクによって、評価すべきポイントが変わるためである。その背景を踏まえて、ここでは広く使われている評価指標をいくつか示す。

Inception Score(IS)

ISは生成データの質と多様性を評価するための指標である。ISを計算するためには、Imagenetで事前学習をしたInception v3を使用する。ISの基本的なアイデアは、以下のとおりである。

  1. Inception v3が簡単に分類できるデータは質の高いデータである(犬画像を生成した際に識別器が自身をもって「犬」クラスに分類できれば、その画像は質が高い)
  2. 生成されたデータ全体に対して、識別器がさまざまなクラスに分類するようにデータが生成されていたら、そのデータ群は多様性をもつ(1000枚画像を生成して、それらが1枚 x 1000クラスに識別器によって分類されたら、その画像群は多様性があるといえる)
  3. 上記1はエントロピーが低い状態であり、上記2はエントロピーが高い状態である。この2条件を満たすとき、上記2つの確率分布間の距離は大きくなる

image.png
引用元:http://www.cv.info.gifu-u.ac.jp/contents/workshop/contents/eccv2018/ppt/how_good_is_my_gan.pdf

ただし、ISにはいくつかの問題点が挙げられる。
一つ目にInception-v3はImageNetで学習しているので、ImageNetのラベルごとに同じ割合で出力するとスコアが上がる。このときに、ラベルごとに同じ画像でもよいということが挙げられる。つまり、ImageNetのデータ分布を真似て、同じ1000クラスの画像を生成すると「多様性」があると判断されてしまう。(本当はImageNet以外にも出力したかったりする)しかも「犬」クラスを出力したい場合、モード崩壊を起こして同じ犬画像を生成し続けると高く評価されてしまう。

二つ目に現実の画像を使用していない点である。あくまでInception-v3がどう判断するかが問題であり、(パラメータとして学習時に反映されているものの)実画像は評価の際には使用されない。

三つ目にInception-v3の分類が完璧であることを仮定している点である。人間からみて明らかに「犬」に見えるのにInception-v3が「猫」と判断してしまうようなことがあれば、そもそもこの指標は成り立たなくなってしまう。

参考 https://data-analytics.fun/2021/12/12/understanding-inception-score/

ISの派生評価指標としては、Mode Score(MS)やmodified-Inception Score(mIS)などがあるが、今回は深煎りはしない。

実データを使用しない評価というISの問題点を解決したのが、Frechet inception distance(FID)である。FIDは以下のように表される。(今回数学的な深煎りはしない)
image.png
引用元:http://www.cv.info.gifu-u.ac.jp/contents/workshop/contents/eccv2018/ppt/how_good_is_my_gan.pdf

学習済みのInception-v3の最後のプーリング層の2048次元ベクトルを埋め込み表現として、実画像と生成画像に対して得たのち、それらが正規分布に従っていると仮定して、それらの2つの正規分布の距離を上記のように計算する。FIDスコアが小さいほど、生成データの分布は実分布に近いとみなす。
参考(https://data-analytics.fun/2021/12/31/understanding-fid/)

Multi-scale structural similarity for image quality(MS-SSIM)

MS-SSIMは、2つの画像の類似度を測る指標の1つであり、エッジやテクスチャ、輝度といった構造的な特徴を基準に類似性を評価する。MS-SSIMはウィンドウサイズを変えたSSIMのN個の平均値で計算される。
image.png
引用元:https://dftalk.jp/?p=18111
SSIMの計算式は上記のように表される。$x$を真の画像、$y$を生成画像とし、それらの平均、分散、今日分散を画像の限られた範囲(ウィンドウサイズ)で行い上記の式で計算する。すると、人間の感覚に近い評価が得られる。MS-SSIMはウィンドウサイズをN個用意し、SSIMをN回計算し、それらの平均を取るらしいが今回はそこまで深入りしない。
MS-SSIMの論文:https://ece.uwaterloo.ca/~z70wang/publications/msssim.pdf

Classifier Two-sample Test(C2ST)

C2STは、実データ集合と生成データ集合の分類を行う2値分類器を作成し、与えられた画像がどちらの集合に属しているものかを当てることができるかどうかで、生成データ分布が実データ分布を再現できているかを評価する手法である。評価の流れは以下の通り。

  1. 学習済みGを用いてデータを生成
  2. 生成されたデータと実データを混ぜ、学習用とテスト用に分割
  3. 分類器を学習用データで学習
  4. テスト用データで学習された分類器を評価

分類器が1Nearest Neigber法だった場合C2ST-1NNと呼び、NNだった場合、C2ST-NNと呼ぶ。
この分類器は事実上Dと同じことをしているので、「フレッシュなデータセットで学習したフレッシュなdiscriminator」と評されている。

C2STの論文"REVISITING CLASSIFIER TWO-SAMPLE TESTS"(https://arxiv.org/pdf/1610.06545.pdf)

Perceptual Path Length(PPL)

PPLはStyle GANの論文"A Style-Based Generator Architecture for Generative Adversarial Networks"で提案された評価指標である。下記ブログでわかりやすくまとめられている。

image.png

上記画像を例とした際の計算手順は以下の通りである。

  1. 2つの潜在空間ベクトル$z_1,z_2$を用意し、比率$t$で混ぜたものと、比率$t+\epsilon$で混ぜたものをそれぞれ作成し、それをシードとしてGでデータを生成(Style GANの論文では$z$をスタイル$w$に変更してから混ぜているが、ここではわかりやすさを優先)
  2. 生成された2つのデータをVGG16を使用して埋め込み
  3. 埋め込まれたベクトル同士の距離を測る

理想的には、潜在空間を変化させた際、白い犬から黒い犬への変化は、ベッドを経由せず、最短距離であってほしい。(そちらの方が人間の感覚に近いため)すなわち、VGG16で埋め込んだベクトル同士の距離が近いかつ、潜在空間の距離も近いという条件を満たせば、それを達成しているとみなせる。(VGG16の埋め込み空間を人間のそれとみなしている?)

Maximun Mean Discrepancy(MMD)

MMDもまた、2つの分布の距離を測る指標である。MMDのスコアが低いほど、2つの分布が類似しているとみなすことができる。以下の式で表される。
$$
M_k(P,Q) = ||\mu_P - \mu_Q|| = E_P[k(x,x')] -2E_{P,Q}[k(x,y)] + E_Q[k(y,y')]
$$
ここで、$P$は実データ分布、$Q$は生成データ分布を表し、$k$はカーネル間数を表す。MMDに関しては以下の記事が参考になる。

Human rank(HR)

人間による評価HRは、他の指標の補完のためや、解決したい問題を評価する際に適した指標が存在しない場合などに用いられる。特性上、人間に理解可能なタスクにのみ評価指標として使用される。例えば、生成された顔画像に対して、本物か偽物かを人間に実際に判断してもらうなどの例があげられる。(人間をDにしている)

さまざまなGANアーキテクチャ

アーキテクチャの最適化

ここでは、さまざまな伝統的なGANアーキテクチャに触れるが、それぞれに深入りはせず、それらの進化に着目することで、包括的な理解を目指す。

Deep Convolutional GAN(DCGAN)

GANが2014年に提案されてから1年後、畳み込み層を活用したCNNによりGANを作成するDCGANが提案された。CNNを活用することで、従来の全結合層と比較し、隣接ピクセルの相関関係の情報をうまく抽出できるようになった。

また、CNNの提案だけでなく、GANの学習を安定させるために、プーリング層により空間次元を減らすのではなく、畳み込み層のストライド数によって空間次元を削減した。

さらに、畳み込み層のフィルターを可視化することで、GANの包括的な学習手法の助けになったことが報告されている。

Conditional GAN(CGAN)

通常のGANは、実データ分布をまねるだけで、どのようなデータを生成するかをユーザーが選べない。これは、Gが学習する潜在空間が人間の感覚と遠いためである。例えば、手書き文字を生成するタスクの場合、理想的には1を生成する潜在空間ベクトル$z_1$は全て近い位置に集中していて欲しい。(それが人間の感覚に近いため)しかし、通常のGANで学習したベクトルがそのようになっているとは限らず、$z_1$がさまざまな場所に散らばっている可能性もある。

そこで提案されたのがCGANである。CGANは通常のD、Gの入力に生成して欲しいデータのラベルを同時に入力することにより、ユーザーは生成する画像のラベルを指定することができ、また潜在空間上でラベルごとにまとまるように学習が進む。目的関数は以下の通りである。

$$
\text{min}_{G} \text{max}_D L(D,G) = E_xlog[D(x|y)] + E_z log[1-D(G(z|y))]
$$
ここで、$x$は分布$p(r)$に従う実データであり、$z$はガウスノイズや一様ノイズの分布$p_z$に従う潜在変数、$y$は生成したいデータのラベルである。なお、$y$の与え方には様々な流派があるらしい。手書き文字の具体例を1つ挙げると、 Gの入力$z$に対してラベルのOne-hotベクトルを結合、Dの入力画像に対して、クラス分のチャネルを追加し、対応するラベルのチャネルの画素値を全て1、それ以外のチャネルは0にするといったものが存在する。CGANは生成データのクラスをユーザーが指定できるので、データ拡張の手段として活用できる。
image.png

以下の記事がとてもわかりやすい。

Auxiliary Classifier GAN(ACGAN)

ACGANはCGANを改良したアーキテクチャである。以下の記事がわかりやすいのでそれを参照。

image.png
CGANとACGANの違いは以下の通りである。

  • CGAN

    • G:入力はクラスラベルと潜在空間ベクトル$z$、出力はデータ$x_{fake}$
    • D:入力はクラスラベルと実データ$x_{real}$ or $x_{fake}$、出力は偽物か本物かのラベル
  • ACGAN

    • G:入力はクラスラベルと潜在空間ベクトル$z$、出力はデータ$x_{fake}$
    • D:入力は実データ$x_{real}$ or $x_{fake}$、出力は偽物か本物かのラベルとデータの所属クラスラベル

画像生成を想定して解釈する。CGANではDはあらかじめどのクラスの画像が来るかを知っており、それが本物かどうかを判断していた。一方ACGANでは、Dはデータだけが入力され、何の画像が来るか分かっていない状態で本物かどうかを判断しつつ、それが何の画像かを当てる必要がある。ACGANで、Dは「本物っぽく見えるかつ、どのクラスであるか判断できる画像」を生成する必要がある。このような設計にすることで、CGANよりも高精度で画像が生成できた。肝となる損失関数は以下の通り。

$$
 L_S = E[\text{log}P(S=real|x_{real}) + \text{log}P(S=fake|x_{fake})]
$$

$$
L_C = E[\text{log}P(C=c|x_{real}) + \text{log}P(C=c|x_{fake})]
$$

Dは$L_C + L_S$を最大化し、Gは$L_C - L_S$を最大化することで、「本物っぽく、何のクラスかわかりやすい偽物画像」を生成できるようになる。

Interpretable Representation Learning by Information Maximizing GANs (InfoGAN)

CGANの登場により、ラベル$c$によって生成画像をある程度コントロールできるようになった。例えば、10クラスの手書き文字文類において $c = (1, 0, 0,...,0)$のOne-hotベクトル入力として与えるとちゃんと0っぽい画像を出力してくれる。これはつまり、Gに与える$c$の空間が人間にとって解釈可能なものになっているということである。10次元ベクトル$c$の各次元は「0っぽさ」、「1っぽさ」〜「9っぽさ」のパラメータの「つまみ」となっているということである。

ところがCGANはデータセットとしてラベルが必要である。つまり、GANの元である「教師なし学習」による生成が消えてしまっている。これを再び教師なし学習でやろうというのがinfoGANである。簡単に説明すると、CGANで使用していたクラスラベルの代わりに潜在空間ベクトル$z$の一部をクラスラベルのようなもの($c_{latent}$)として扱ってGで画像を生成し、Dは本物かどうかの判断と入力の$c_{latent}$の予測を行う。このようにすることで、$c$が視覚的に理解可能なパラメータの「つまみ」として機能するようにモデルが学習する。(右から2つ目)

image.png
infoGANの損失関数は以下のように表される。

$$
\text{min}_{G,Q}\text{max}_DV(D,G,Q) = V(D,G) - \lambda L_I(G,Q)
$$

ここで、$V(D,G)$は通常のGANと同様の損失関数である。よって、info GANの特徴は$\lambda L_I(G,Q)$にある。$\lambda$はハイパーパラメータである。また、$L_I(G,Q)$は相互情報量$I(c;G(z,c))$の下界であり、$L_I(G,Q) \leq I(c;G(z,c))$である。

相互情報量は、$I(c;G(z,c)) = H(c) - H(c|G(z,c))$である。$H$はエントロピーを表す。つまり、相互情報量とは、$p(c)$の情報量と$p(c|G(z,c))$の差となる。

Dは、$L_I(G,Q)$が小さくなるように学習を進めるように損失関数は設計されている。その結果、$I(c;G(z,c))$も小さくなっていく。$I(c;G(z,c))= H(c) - H(c|G(z,c))$の中で、$G$が絡んでいるのは、 $- H(c|G(z,c))$だけであるから、$H(c|G(z,c)$が大きくなり最大の$H(c)$に近づくように学習が進むということである。これは、何を意味するのかというと、Gが生成した画像だけを見て、Dが$c$がなんだったかを当てられる状態を指す。このような状態になると、$c$の潜在空間は人間に理解可能な空間となる。

なお、$Q$の話は出てきていないが、関数$Q$とは、生成された画像の$c$を当てる関数のことであり、上画像では、Dの中に統合されている(されていない場合もある)。Qの損失関数としては、KLダイバージェンスが使用される。

また、L_Iが具体的にどのような関数かについて言及していないが、ここまで言及すると長くなるため、あとは論文を参照して欲しい。

Image-to-Image Translation with Conditional Adversarial Nets(Pix2Pix)

Pix2Pixは、CGANを画像から画像の変換に使用した技術である。その応用先としては、セマンティックセグメンテーションのアノテーションマスクからの画像生成、航空画像からのGoogleマップ生成、白黒画像からカラー画像の生成など多岐にわたる。

image.png

Pix2Pixの元論文では、損失関数を以下のように定義している。

$$
L_{\text{Pix2Pix}} = \text{argmin}_G \text{max}_DL_{CGAN}(G,D) + \lambda L_{L1}(G)
$$
image.png

これはもとのCGANの損失関数に、Gが生成する画像と教師データの画像の$L_1$ノルムを加えたものである。$L_1$ノルムを使用すると画像のぼやけがすくなくなるらしい。GとしてはU-Netを使用した。例えば、セマンティックセグメンテーションのマスクから画像を作成する際、車と道路の境目をどこにするかなどの情報は、抽象化される前の入力画像が一番多く持っている。つまり、元の入力の特徴を出力に反映させたい場合、低レベル情報を出力に反映させやすいskip-connectionを持つUNetが適していると言える。

画像に多様性を持たせるために、Gに対し、ノイズ$z$を入力しているが、Gはだんだんそれを無視して画像を変換してしまうらしい。(例えば、白黒画像のカラー化タスクを解く場合、同じ白黒画像に対しては同じ色の付け方しかしないなど)この問題点は本論文中では解決できていない。論文は以下の通り。

Cycle-Consistent GAN(CycleGAN)

Cycle GANの目的は、教師なし学習により、ドメインAの画像からドメインBの画像に変換を行うことである。例えば、セマンティックセグメンテーションマスクからの画像の生成を教師なし学習により行いたいというモチベーションがある。(Pix2Pixはこれを教師あり学習で実現)元論文から引用した画像が以下のものである。PairedがPix2Pixで使用する教師あり学習のデータで、Unpaired がCycleGANで使用する教師なし学習の例である。

image.png

CycleGANは、Cyclic consistency(循環一貫性)というアイディアに基づいている。これは、ドメインAのデータ$x$がドメインBに変換される際、変換されたデータ$x$はドメインAに逆変換した際に元の状態を再現できるべきであるという考えである。

image.png

損失関数は以下のように定義される。
$$
L(G,F,D_X,D_Y) = L_{GAN}(G,D_Y,X,Y) + L_{GAN}(F,D_X,Y,X) + \lambda L_{cyc}(G,F)
$$
ここで、$G$はドメインAからBの画像を生成するGenerator、$F$はドメインBからAの画像を生成するGenerator、$D_X, D_Y$はそれぞれドメインA、BそれぞれのDiscriminatorである。$L_{GAN}$はそれぞれのドメインに対する敵対的損失を表している。$L_{cyc}$は、サイクル一貫性損失と呼ばれる項であり、ドメインA→B→A(B→A→B)と変換されたデータにおいて変換前と変換後が遠いほど大きくなる損失である。具体的には以下のようになる。

$$
L_{cyc}(G,F) = E_{x \sim p_{data}(x)}[||F(G(x) - x||_1] + E_{y \sim p_{data}(y)}[||G(F(y) - y||_1]
$$

CycleGANは教師なし学習にも関わらず、Pix2Pixと同等程度の精度を出せるらしい。以下の記事を参考にした。

Learning to Discover Cross-Domain Relations with GANs (DiscoGAN)

基本的な考えはCycle GANと一緒で再構成損失(~=サイクル一貫性損失)により、全単射の変換を獲得できるように損失関数を設計している。Disco GAN の特徴は、オートエンコーダーをGとして使用し、DとしてGのエンコーダーをベースとした分類器を使用していることである。オートエンコーダーはもともと画像の特徴を圧縮したのち、再構成することを目的してといるものであり、ドメイン間変換においても「必要な特徴を維持しつつ圧縮する」という操作は共通している。
Disco GANは良い記事が見つからなかったので自分で生成した。興味がある場合は以下を参照して欲しい。

GANILLA

GANILLAはスタイル(style)と内容(content)を共に維持したまま、データを変換する手法である。元論文より以下の画像を引用する。

image.png

上記の画像は3種の学習データをUnpairとするGANの比較である。Cycle GANはスタイルを変換できているが、構造を保存できていない、一方DualGANは構造は保存できているが、スタイルを変換できていない。GANILLAは建物の構造を維持しつつ、スタイルを変更することに成功している。

GANILLA損失関数は、Cycle GANとほぼ同じで、サイクル一貫性損失 + 敵対的損失である。サイクル一貫性損失にはL1ノルムを使用している。GANILLAのミソはGのモデル構造にある。

image.png

skipコネクション(Figure 2の青矢印)を使用して、エンコーダーの出力とデコーダーの出力を結合(concatnate)することで、低レベルな特徴をでコード処理に活用する。ここでいう低レベルな特徴というのは、エッジや形などを指す。一方、4層にわたるエンコーダーにより、テンソルは高レベルな特徴が圧縮されていく。ここでいう高レベルな特徴とはスタイルを指す。その結果、スタイルと内容を両方反映した変換を実現している。

downsamplingのステージでは、ResNet-18を使用しているらしい。Dとしては、PatchGANを使用しているらしいが、ここでは説明は元論文に譲る。

まとめ

記事が長くなってしまったので一旦終了する。本記事では、以下の内容についてまとめた。

  • GANの基本設計
  • GANの問題点
  • GANの評価方法
  • さまざまな種類のGAN
    • Conditional GAN系統
    • Cycle GAN 系統
      この先の内容はまた別の記事にまとめようと思います。何か間違い等ありましたらご指摘いただけますと幸いです。
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1