オートエンコーダとVAE

著者:Sherlock
リンク:https://zhuanlan.zhihu.com/p/27549418
出典:
著者がほぼ著作権を所有していることを知っています。商用転載の場合は、著者に連絡して許可を求めてください。非商用転載の場合は、出典を明記してください。
 

オートエンコーダとは

AutoEncoderは元々データ圧縮方法として使用されていましたが、その特徴は次のとおりです。

1)データとの相関度が非常に高いため、オートエンコーダはトレーニングデータと同様のデータしか圧縮できません。ニューラルネットワークによって抽出された特徴は一般に元のデータとの関連性が高いため、これは実際にはより明白です。顔を使用したトレーニングセットトレーニング済みのオートエンコーダは、人間の顔の特性のみを学習し、自然の画像の特性は学習しないため、自然動物の写真を圧縮する場合のパフォーマンスが低下します。

2)次元削減の過程で情報が必然的に失われるため、データは圧縮後に非可逆になります。

2012年に、畳み込みネットワークでオートエンコーダーを使用してレイヤーごとの事前トレーニングを行うと、より深いネットワークをトレーニングできることがわかりましたが、すぐに、優れた初期化戦略が、激しいレイヤーごとの事前トレーニングよりもはるかに効果的であることがわかりました。 2014年には、バッチ正規化テクノロジーの出現も効果的にトレーニングできるより深いネットワークです。15日の終わりまでに、基本的に残差(ResNet)を介して任意の深さのニューラルネットワークをトレーニングできます。

したがって、オートエンコーダの主な用途は2つあります。1つはデータのノイズ除去であり、もう1つは視覚的な次元削減です。ただし、オートエンコーダのもう1つの機能は、データを生成することです。

GANについては前に説明しましたが、GANと比較すると、いくつかの利点がありますが、いくつかの欠点もあります。まず、GANと比較した場合の利点について説明します。

最初のポイントは、GANを使用して画像を生成することです。欠点は、ランダムなガウスノイズを使用して画像を生成することです。つまり、指定したタイプの画像を生成できないため、どちらを使用するかを決定できません。これは、すべての初期分布を試すことができない限り、ある種のランダムノイズが必要な画像を生成する可能性があります。しかし、オートエンコーダーを使用すると、出力画像のエンコードプロセスを通じてエンコード後にこのタイプの画像の分布を取得できます。これは、各画像に対応するノイズ分布を知ることと同等であり、特定のノイズを選択することで必要なものを生成できます。 。生成される画像。

第2のポイントは、これは生成ネットワークの利点であるだけでなく、一定の制限もあるということです。これは、生成ネットワークが対立プロセスを通じて「本物の」画像と「偽の」画像を区別することですが、この方法で取得された画像はは可能な限りリアルです。しかし、これは画像のコンテンツが私たちが望むものであることを保証するものではありません。言い換えれば、ネットワークを可能な限り生成して、いくつかの背景パターンを生成し、それを可能な限り真実にすることが可能です。可能ですが、実際のオブジェクトは含まれていません。

オートエンコーダの構造

まず、オートエンコーダの一般的な構造を示します

上の図から、2つの部分を見ることができます。最初の部分はエンコーダー(エンコーダー)、2番目の部分はデコーダー(デコーダー)です。エンコーダーとデコーダーは任意のモデルにすることができます。通常、エンコーダーとデコーダーとしてニューラルネットワークモデルを使用します。 。入力データはニューラルネットワークによってコードに変換され、別のニューラルネットワークによってデコードされて、元の入力データとまったく同じ生成データが取得されます。次に、2つのデータを比較することにより、両者の違いが最小限に抑えられます。このネットワークのエンコーダーとデコーダーのパラメーターをトレーニングします。このプロセスのトレーニングの後、デコーダーを取り出してコード(コード)をランダムに渡すことができます。デコーダーを介して元のデータと同様のデータを生成することを期待しています。上の図の例は、Almostを生成できるようにすることです。同じ写真。

これは起こり得ますか?実際、それは可能です。以下では、PyTorchを使用してオートエンコーダーを実装します。

まず、それを実現するために単純な多層パーセプトロンを構築します。

class autoencoder(nn.Module):
    def __init__(self):
        super(autoencoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Linear(28*28, 128),
            nn.ReLU(True),
            nn.Linear(128, 64),
            nn.ReLU(True),
            nn.Linear(64, 12),
            nn.ReLU(True),
            nn.Linear(12, 3)
        )
        self.decoder = nn.Sequential(
            nn.Linear(3, 12),
            nn.ReLU(True),
            nn.Linear(12, 64),
            nn.ReLU(True),
            nn.Linear(64, 128),
            nn.ReLU(True),
            nn.Linear(128, 28*28),
            nn.Tanh()
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

ここでは、単純な4層ネットワークをエンコーダーとして定義し、中央のReLU活性化関数を使用して、最終的な出力次元は3次元、定義されたデコーダー、3次元コードの入力、28x28の画像データの出力、特別な支払い最後に注意してください使用される活性化関数はTanhです。この活性化関数は最終出力を-1から1の間に変換できます。これは、入力した画像が-1から1の間に変換されており、ここでの出力はに対応している必要があるためです。それ。

トレーニングプロセスも比較的単純です。最小平均二乗誤差を損失関数として使用して、生成された画像と元の画像の各ピクセルの差を比較します。

同時に、多層パーセプトロンを畳み込みニューラルネットワークに置き換えることもできます。これにより、画像の特徴抽出により良い効果が得られます。

class autoencoder(nn.Module):
    def __init__(self):
        super(autoencoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, 3, stride=3, padding=1),  # b, 16, 10, 10
            nn.ReLU(True),
            nn.MaxPool2d(2, stride=2),  # b, 16, 5, 5
            nn.Conv2d(16, 8, 3, stride=2, padding=1),  # b, 8, 3, 3
            nn.ReLU(True),
            nn.MaxPool2d(2, stride=1)  # b, 8, 2, 2
        )
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(8, 16, 3, stride=2),  # b, 16, 5, 5
            nn.ReLU(True),
            nn.ConvTranspose2d(16, 8, 5, stride=3, padding=1),  # b, 8, 15, 15
            nn.ReLU(True),
            nn.ConvTranspose2d(8, 1, 2, stride=2, padding=1),  # b, 1, 28, 28
            nn.Tanh()
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

ここでは、nn.ConvTranspose2d()が使用されます。これは、ある意味でデコンボリューションと見なすことができる畳み込みの逆演算と見なすことができます。

畳み込みネットワークを使用して取得した最終的な画像の方が優れています。ここでは特定の画像効果を示しません。画像の表示はgithubで確認できます。

変分オートエンコーダ

変分エンコーダーは、オートエンコーダーのアップグレード版であり、その構造はオートエンコーダーと同様であり、エンコーダーとデコーダーで構成されています。

オートエンコーダで行ったことを思い出してください。画像を入力してから、画像をエンコードして非表示のベクトルを取得します。これは、元の画像が含まれているため、ランダムノイズをランダムに選択するよりも優れています。次に、ベクトルを暗黙的にデコードして、元の画像に対応する写真。

しかし、このように、自分で隠しベクトルを構築する方法がないため、実際に画像を任意に生成することはできません。隠しベクトルが何であるかを知るために、画像を介してエンコーディングを入力する必要があります。その後、変分を使用できます。オートエンコーダこの問題を解決するため。

実際、原理は非常に単純です。エンコードプロセスでいくつかの制限を追加するだけで、生成される暗黙のベクトルが標準の正規分布にほぼ従うようになります。これが、一般的なオートエンコーダーとの最大の違いです。

このように、新しい画像を生成するのは非常に簡単です。標準の正規分布のランダムな非表示ベクトルを与えるだけで、デコーダーは元の画像を与えずに必要な画像を生成できます。最初にコードを記述します。

実際の状況では、モデルの精度と暗黙のベクトルが標準正規分布に従うことの間でトレードオフを行う必要があります。モデルの精度とは、デコーダーによって生成された画像と元の画像との類似度を指します。 。ネットワークにこの決定を任せることができます。それは非常に簡単です。両方の損失を出し、それを合計損失として合計するだけで、ネットワークはこの合計損失をどのように行うかを選択できます。減少します。さらに、2つの分布の類似性を測定する必要があります.2つの分布の類似性を測定するためにKL発散と呼ばれるものがあることを以前にGANの数学的導出をどのように読みますか、ここではKLを使用します暗黙的な表現を表すための発散ベクトルと標準正規分布の差の損失、およびその他の損失は、生成された画像と元の画像の平均二乗誤差によって表されます。

KL発散の公式を与えることができます

[公式]

ここで、変分エンコーダーは、「再パラメーター化」の手法を使用して、KL発散の計算問題を解決します。

このとき、毎回非表示のベクトルを生成する代わりに、平均値と標準偏差を表す2つのベクトルが生成され、これら2つの統計を使用して暗黙のベクトルが合成されます。これも、標準を使用すると非常に簡単です。正規分布は、最初に標準偏差で乗算され、次に平均に追加されます。ここでは、正規分布に従うように、エンコード後に暗黙のベクトルをデフォルト設定します。このとき、平均をできるだけ0に近づけ、標準偏差をできるだけ1に近づけたいと考えています。そして、この損失の計算式を取得する方法についての論文には詳細な導出があり、興味のある学生はその導出を見に行くことができます

以下はPyTorchの実装です

reconstruction_function = nn.BCELoss(size_average=False)  # mse loss

def loss_function(recon_x, x, mu, logvar):
    """
    recon_x: generating images
    x: origin images
    mu: latent mean
    logvar: latent log variance
    """
    BCE = reconstruction_function(recon_x, x)
    # loss = 0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)
    KLD_element = mu.pow(2).add_(logvar.exp()).mul_(-1).add_(1).add_(logvar)
    KLD = torch.sum(KLD_element).mul_(-0.5)
    # KL divergence
    return BCE + KLD

さらに、変分エンコーダーを使用すると、隠れた変数をランダムに生成できるだけでなく、ネットワークの一般化機能も向上します。

最後に、VAEのコード実装

class VAE(nn.Module):
    def __init__(self):
        super(VAE, self).__init__()

        self.fc1 = nn.Linear(784, 400)
        self.fc21 = nn.Linear(400, 20)
        self.fc22 = nn.Linear(400, 20)
        self.fc3 = nn.Linear(20, 400)
        self.fc4 = nn.Linear(400, 784)

    def encode(self, x):
        h1 = F.relu(self.fc1(x))
        return self.fc21(h1), self.fc22(h1)

    def reparametrize(self, mu, logvar):
        std = logvar.mul(0.5).exp_()
        if torch.cuda.is_available():
            eps = torch.cuda.FloatTensor(std.size()).normal_()
        else:
            eps = torch.FloatTensor(std.size()).normal_()
        eps = Variable(eps)
        return eps.mul(std).add_(mu)

    def decode(self, z):
        h3 = F.relu(self.fc3(z))
        return F.sigmoid(self.fc4(h3))

    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparametrize(mu, logvar)
        return self.decode(z), mu, logvar

VAEの結果は、通常のオートエンコーダの結果よりもはるかに優れています。以下は結果です。

 

VAEの欠点も明らかです。GANのように戦って学習するのではなく、生成された画像と元の画像の間の平均二乗誤差を直接計算するため、生成された画像が少しぼやけます。VAEの構造を使用して、VAEとGANを組み合わせた作業はすでにいくつかありますが、トレーニングに敵対的ネットワークを使用している場合は、このペーパーで詳細参照できます。

おすすめ

転載: blog.csdn.net/a493823882/article/details/106986898