深層学習におけるオートエンコーダー

ノイズの多いデータは、私たちデータ サイエンティストを夜更かしさせる最も一般的な機械学習の問題の 1 つです。
高次元の非構造化データ(画像、音声、テキスト)、深層学習に対して、いかに特徴情報を取得するかが最大の課題でもあります。
しかし幸いなことに、現在では、データの次元削減と再構成にさまざまな手法とトリックを利用できます。その 1 つがオートエンコーダーです。

オートエンコーダーとは

オートエンコーダーは、データ圧縮に使用される人工ニューラル ネットワークの一種で、入力データを小さなコードに圧縮し、元のデータにデコードすることができます。ラベル付きデータを必要としないため、教師なし学習アルゴリズムと見なすことができます。

ここに画像の説明を挿入

オートエンコーダーの構造

オートエンコーダーのアーキテクチャーの簡単な概要から始めましょう。オートエンコーダーは、次の 3 つの部分で構成されます。

  1. エンコーダー: トレーニング検証テスト セットの入力データを、通常、入力データよりも数桁小さいエンコードされた表現に圧縮するモジュール。
  2. ボトルネック: 圧縮された知識表現が含まれているため、ネットワークの最も重要な部分です。
  3. デコーダー: ネットワークが知識表現を「解凍」し、エンコードされた形式からデータを再構築するのを支援するモジュール。その後、グラウンド トゥルースと比較されます。

アーキテクチャ全体は次のようになります。
ここに画像の説明を挿入

エンコーダー、ボトルネック、デコーダーの関係

エンコーダー エンコーダーは、モデルの入力をボトルネックと呼ばれるコンパクトな部分に圧縮するプーリング ブロックが続く畳み込みブロックのセットです。

ボトルネックの背後にあるのは、圧縮された特徴を画像形式に戻す一連のアップサンプリング モジュールで構成されるデコーダです。

単純なオートエンコーダーの場合、出力は入力と同じであることが期待されますが、ノイズが除去されます。

しかし、変分オートエンコーダーの場合、出力は、モデルによって提供される入力情報を使用して形成された、まったく新しい画像です。

ボトルネック ニューラル ネットワークの最も重要な部分であり、最小の部分でもあるのがボトルネックです。ボトルネックは、エンコーダからデコーダへの情報の流れを制限するために存在し、最も重要な情報のみが通過できるようにします。ボトルネックは画像​​と同じくらい多くの情報をキャプチャするように設計されているため、ボトルネックは入力の知識表現を形成するのに役立つと言えます。

したがって、エンコーダー/デコーダー構造は、画像から最も有用なデータを抽出し、ネットワーク内のさまざまな入力間の有用な関係を確立するのに役立ちます。入力の圧縮された表現としてのボトルネックは、ニューラル ネットワークが入力を記憶し、データに過剰適合することをさらに防ぎます。経験則として、ボトルネックが小さいほど、オーバーフィッティングのリスクが低くなることに注意してください。ただし、ボトルネックが非常に小さいと、保存できる情報量が制限されるため、重要な情報がエンコーダーのプーリング レイヤーをすり抜ける可能性が高くなります。デコーダー 最後に、デコーダーは、ボトルネックの出力を再構築するために使用される一連のアップサンプリング ブロックと畳み込みブロックです。デコーダーへの入力は圧縮された知識表現であるため、デコーダーは「解凍器」として機能し、その潜在的なプロパティからイメージを構築します。

オートエンコーダーのトレーニング方法

オートエンコーダーをトレーニングする前に、次の 4 つのハイパーパラメーターを設定する必要があります。

  • エンコード サイズ: エンコード サイズまたはボトルネック サイズは、オートエンコーダーを調整するための最も重要なハイパーパラメーターです。ボトルネックのサイズによって、データをどれだけ圧縮する必要があるかが決まります。これは、正則化用語としても機能します。
  • レイヤー数: すべてのニューラル ネットワークと同様に、オートエンコーダーを調整するための重要なハイパーパラメーターは、エンコーダーとデコーダーの深さです。深度が高いほどモデルの複雑さが増し、深度が低いほど処理が速くなります。
  • レイヤーごとのノード: レイヤーごとのノードの数は、各レイヤーに使用する重みを定義します。通常、オートエンコーダーの後続の各レイヤーのノード数は、各レイヤーへの入力がレイヤー全体で小さくなるにつれて、徐々に減少します。
  • 再構築損失: オートエンコーダーのトレーニングに使用する損失関数は、オートエンコーダーを適応させたい入力と出力のタイプに大きく依存します。画像データを扱う場合、再構成に最も一般的に使用される損失関数は、平均二乗誤差 (MSE 損失) と L1 損失です。MNIST データセットのように、入力と出力が [0, 1] の範囲にある場合は、バイナリ クロス エントロピーを再構成損失として使用することもできます。

最後に、さまざまな種類のオートエンコーダーについて見ていきましょう。

5種類のオートエンコーダ

オートエンコーダーの概念は新しいものではありません。実際、最初のアプリケーションは 1980 年代にさかのぼることができます。もともと次元削減と特徴学習に使用されていたオートエンコーダーの概念は、時間の経過とともに、データの生成モデルを学習するために広く使用される手法に進化しました。一般的なオートエンコーダーの 5 つのタイプを次に示します。

1.不完全なオートエンコーダー

アンダーコンプリート オートエンコーダーは最も単純なオートエンコーダーの 1 つであり、1980 年代に次元削減と特徴学習に初めて適用されました。その原理は単純で、入力データを圧縮して一種の潜在空間を生成し、それを解凍して元のデータに戻します。教師なし学習であるため、ラベルは必要ありません。アンダーコンプリート オートエンコーダーは、高次元のデータを低次元の潜在空間に射影する次元削減手法と見なすことができます。

その計算式:
入力データxxx はエンコーダfff は潜在空間特徴ベクトルhh

h = f ( x ) h=f(x)時間=f ( x )
次に、潜在空間の特徴ベクトルhhh はデコーダggg再構築されたデータを生成するx ‘ x’バツ'
x ' = g ( h ) x'=g(h)バツ=g ( h )

不完全オートエンコーダーの損失関数は再構成誤差であり、L1 損失や平均二乗誤差など、さまざまな異なる誤差関数を使用して表すことができます。

不完全オートエンコーダーを使用することで、高次元データを低次元の潜在空間に圧縮し、元のデータを再構築することができます。この手法は、画像処理、音声信号処理、自然言語処理などの分野で実際に非常に役立ちます。他の次元削減手法と比較して、アンダーコンプリート オートエンコーダーは非線形関係を学習できるため、データ情報を保持しながらより優れた次元削減を実行できます。

このモデルを人に例えると、旅行に持って行ける荷物はひとつですが、できるだけ多くの荷物を持っていく必要があります。そのため荷物の詰め方を考えたり、目的地に着いたら荷物を組み立てたりする必要があります。不完全なオートエンコーダーは、データを圧縮して再構築するのに役立つ「バゲージ」です。

2.スパースオートエンコーダー

スパース オートエンコーダは、アンダーコンプリート オートエンコーダを拡張したもので、アンダーコンプリート オートエンコーダと比較して、データの特性をよりよく学習できるスパース制約が追加されていることが特徴です。スパース オートエンコーダのアルゴリズム原理と計算式は次のとおりです。
ここに画像の説明を挿入

アルゴリズムの原理

スパース オートエンコーダーは、非表示層のニューロンの平均活性化を制限し、一部のニューロンのみを活性化するように強制するため、モデルがより堅牢になります。この制約は、目的関数にペナルティ項を追加することで実現できます。

計算式

スパース オートエンコーダの場合、目的関数は、再構成誤差とスパース ペナルティの 2 つの部分で構成されます。

再構成エラーの部分は、不完全オートエンコーダーと同じです。つまり、ネットワークは、入力と出力の間のエラーを最小化することによってトレーニングされ、式は次のようになります。 J 再構成 ( W , b ; x ( i ) )
= 1 2 ∣ ∣ y ( x ( i ) ) − x ( i ) ∣ ∣ 2 J_{reconstruction}(W,b;x^{(i)}) = \frac{1}{2}||y(x ^{(i)} ) - x^{(i)}||^2J構成_ _ _ _ _ _ _ _ _( W ,b ;バツ( i ) )=21∣∣ y ( x( i ) )バツ( i )2

其中, W W Wbbbはネットワークの重みとバイアスの項x ( i ) x^{(i)}バツ( i )はトレーニング データ セットのii 番目サンプルi , y ( x ( i ) ) y(x^{(i)})y ( x( i ) )はネットワークの出力です。

スパース性ペナルティ部分は、スパース性制約を追加することで実現でき、その式は次のとおりです。

J sparse ( a ) = ∑ j = 1 s KL ( ρ ∣ ∣ ρ j ^ ) J_{sparse}(a) = \sum_{j=1}^{s}KL(\rho || \hat{\rho_j }))Jまばら_ _( a )=j = 1sK L ( ρ ∣∣r^)

その中でもs.sは隠れ層のニューロン数、aaaは隠れ層の出力ρ \rhoρは予想されるニューロンの活性化、ρ j ^ \hat{\rho_j}r^計算された平均アクティベーションです。KL ( ρ ∣ ∣ ρ j ^ ) KL(\rho || \hat{\rho_j})K L ( ρ ∣∣r^)は、次の式で計算できる KL ダイバージェンスを表します
。 || \ hat{\rho_j}) = \rho log \frac{\rho}{\hat{\rho_j}} + (1 - \rho)log\frac{1-\rho}{1-\hat{\ rho_j}}K L ( ρ ∣∣r^)=ρl o gr^r+( 1r )ログ_ _1r^1r

目的関数では、スパース ペナルティ項の重みをハイパーパラメーターで調整できます。

稀疏自动编码器可以类比于人脑的学习过程。人脑在学习新事物时,会尝试着找到其中的一些特征来加深对事物的理解。同样,稀疏自动编码器通过强制网络只学习部分特征,使得网络更具有鲁棒性和泛化能力,从而能够更好地学习和理解输入数据。

3.收缩式自动编码器

收缩式自动编码器(Contractive Autoencoder)是一种可以学习数据的低维表示的无监督学习算法。相较于欠完备自动编码器和稀疏自动编码器,收缩式自动编码器更关注于数据的局部结构。

该算法的出现可以追溯到 Rifai 等人在 2011 年的一篇论文《Contractive Auto-Encoders: Explicit Invariance During Feature Extraction》,论文提出的收缩式自动编码器主要是在欠完备自动编码器的基础上加入了对数据的局部结构进行约束,使得编码器对于微小变化的鲁棒性更好,从而学习到更加稳定和具有可解释性的低维表示。

算法原理

收缩式自动编码器在欠完备自动编码器的基础上,添加了一个额外的项来惩罚网络对输入数据的微小变化。这个额外的项是通过计算编码层对输入数据的 Jacobian 矩阵 Frobenius 范数得到的。Frobenius 范数衡量了矩阵每个元素的平方和的平方根,因此这个额外的项衡量的是编码器在输入数据微小变化时对于编码的微小变化,从而实现对数据局部结构的约束。
ここに画像の説明を挿入

计算公式

W W W 为编码器中的权重参数, h ( x ) h(x) h(x) 为编码器的输出, J J J h ( x ) h(x) h ( x )xxxのヤコビ行列、フロベニウス ノルムの計算式は次のとおりです。

∣ ∣ J ∣ ∣ F 2 = ∑ i , j ( ∂ hj ( x ) ∂ xi ) 2 ||J||_F^2=\sum_{i,j}(\frac{\partial h_j(x)}{ \partial x_i})^2∣∣ J 2=j(×∂h _( × ))2

収縮オートエンコーダーの損失関数は一般に 2 つの部分を含みます. 1 つは再構成エラー (再構成エラー) で、もう 1 つはエンコード層の制約 (収縮ペナルティ) です. 通常の式は次のとおりです:

L = 1 N ∑ i = 1 N ∣ ∣ xi − g ( f ( xi ) ) ∣ ∣ 2 2 + λ ∣ ∣ J ∣ ∣ F 2 \mathcal{L}=\frac{1}{N}\sum_{ i=1}^N||x_i-g(f(x_i))||_2^2+\lambda||J||_F^2L=N1私は= 1N∣∣ ×g ( f ( x)) 22+λ ∣∣ J 2

どこgはデコーダ、fffはエンコーダ、xi x_iバツは入力データ、NNNはサンプル数、λ \lambdaλは拘束係数で、再構成誤差と拘束項のバランスをとるために使用されます。

絵を描く方法を学んでいるのに、自分のスタイルには特定の要素が欠けていると感じていると想像してください。あなたはブートキャンプに参加して、人の顔を上手に描く方法を学ぶことにしました。トレーニングを開始する前に、これらの機能を説明する方法と、それらを組み合わせて完全な顔をペイントする方法を理解する必要があります。

これは、画像から最も重要な特徴を学習し、データを圧縮するときにそれらの特徴のみを保持する縮小オートエンコーダーのようなものです。このようにして、データを圧縮しながら重要な情報を保持できます

4. オートエンコーダのノイズ除去

ここに画像の説明を挿入

Denoising Autoencoder (デノイジング オートエンコーダー) は、ノイズを除去してデータの特徴を抽出できるオートエンコーダーです。その出現は、実環境でノイズによってデータが乱されることが多いという問題を解決することです。

アルゴリズムの原理

去噪自动编码器与标准自动编码器的不同之处在于它们的训练数据是被添加了随机噪声的数据。去噪自动编码器在输入数据中添加随机噪声,然后将经过噪声处理后的数据作为输入,重构清晰数据。在这个过程中,它不仅仅要学习数据的特征,还要学习去除噪声的技巧,因此更加健壮。

计算公式

去噪自动编码器的计算公式与标准自动编码器的公式类似,只是多了一个去噪项。其公式如下:

L θ = ∑ i = 1 n ∣ ∣ g θ ( x i ~ ) − x i ∣ ∣ 2 \mathcal{L}_{\theta}=\sum_{i=1}^n||g_\theta(\widetilde{\mathbf{x_i}})-\mathbf{x_i}||^2 Lθ=i=1n∣∣gθ(xi )xi2

其中, x i ~ \widetilde{\mathbf{x_i}} xi 是被添加了随机噪声的数据, g θ g_{\boldsymbol{\theta}} gθ 是编码器和解码器的函数, L D A E \mathcal{L}_{DAE} LDAE 是训练损失函数。

拟人解释:去噪自动编码器就像是我们在嘈杂的环境中学习一件事情,我们会尽可能地去掉噪声,把一些关键信息留下来。例如,我们在嘈杂的音乐会上,虽然听不清每个音符,但我们仍然可以听到一首歌的主旋律。去噪自动编码器的原理也是一样的,它通过训练,可以从受噪声干扰的数据中提取出有用的信息,并去除噪声。

5. Variational Autoencoder VAE (生成モデル用)

Variational Autoencoder (VAE) は、データの確率分布を学習し、新しいデータ サンプルを生成できるようにすることを主な目的とするニューラル ネットワーク ベースの生成モデルです。VAE は、2013 年に Diederik P. Kingma と Max Welling によって最初に提案されたもので、従来のオートエンコーダー (Autoencoder、AE) を改良したものです。

従来のオートエンコーダーでは、エンコーダーとデコーダーの両方が決定論的関数であり、モデルは再構成エラーを最小限に抑えることによってトレーニングされます。一方、VAE は確率ベースの生成モデルを導入して、入力データの基になる分布を記述します。VAE はデータを潜在変数ベクトルにエンコードし、デコーダーを使用してこのベクトルを元のデータ空間にマッピングします。決定論的エンコーダーとデコーダーを使用する従来のオートエンコーダーとは異なり、VAE はランダム エンコーダーを使用して入力データを潜在変数の分布にエンコードし、サンプリングによって新しいデータ サンプルを生成します。

VAE の目標は、潜在変数の分布が標準正規分布に従うように制約しながら、再構成エラーを最小限に抑えることです。これは、再構成誤差と、潜在変数分布と標準正規分布の差を測定する KL ダイバージェンスを最小化することによって達成されます。この方法により、VAE は多様性と連続性を備えたデータ サンプルを生成でき、データ生成の多様性の程度を制御できます。

アルゴリズムの原理

VAE は、データの生成と再構築のためにデータ分布の低次元表現を学習するために使用できる生成モデルです。VAE は Autoencoder (Autoencoder、AE) の基本構造に基づいていますが、潜在空間での連続分布を学習できるようにする別のトレーニング戦略を使用します。このトレーニング戦略は、変分推論 (VI) に基づいています。

VAE は、エンコーダーとデコーダーを使用します。エンコーダーはデータxxを入力しますx は、潜在空間の潜在変数zzz、デコーダは潜在変数zzz は、再構成されたデータx ‘ x’バツ' . このように、VAE は入力データxxxは潜在空間の潜在変数zzz、次に隠し変数zzz は、再構成されたデータx ‘ x’バツ'関数、つまり:

x ' = f θ ( z ) , z = g ϕ ( x ) x'=f_\theta(z),z=g_\phi(x)バツ=( z ) ,=gφ( × )

フォーム、f θ f_{\theta}デコーダを表すg ϕ g_{\phi}gφエンコーダを表すθ \thetaθϕ \phiϕ は、デコーダとエンコーダのパラメータを示します。

VAE をトレーニングするには、対数尤度関数log ⁡ p θ ( x ) \log p_{\theta}(x) を最大化する必要があります。ログ_p( × ) . ただし、対数尤度関数を直接計算することは実行z値の積分したがって、VAE は変分推論を使用して、問題を下限の最大化に変換します。

LVAE = E q ϕ ( z ∣ x ) [ logp θ ( x ∣ z ) ] − KL ( q ϕ ( z ∣ x ) ∣ ∣ p ( z ) ) \mathcal{L}_{VAE}=E_{q_\ phi(z|x)}[log p_\theta(x|z)]-KL(q_\phi(z|x)||p(z))LVAE _ _=qφ( z x )[ログp _ _( x z )]K L ( qφ( z x ) ∣∣ p ( z ))

ここでq ϕ ( z ∣ x ) q_{\phi}(z|x)qφ( z x ) は与えられたxxxzzz , p ( z ) p(z)の事後確率分布p ( z )は事前分布を表し、KL ( q ϕ ( z ∣ x ) ∣ ∣ p ( z ) ) \text{KL}(q_{\phi}(z|x)||p(z))KL ( qφ( z x ) ∣∣ p ( z ))は事後分布q ϕ ( z ∣ x ) q_{\phi}(z|x) をqφ( z x )と事前分布p ( z ) p(z)p ( z )間の KL ダイバージェンス

この下限は、再構成誤差と正則化項の 2 つの部分に分解できます。再構成エラーは、デコーダで再構成されたデータx ' x'を測定します。バツ'と生データxxxの差。正則化項は事後分布q ϕ ( z ∣ x ) q_{\phi}(z|x) をqφ( z x )は事前分布p ( z ) p(z)に近いp ( z )

アルゴリズム処理

以下は、VAE トレーニング プロセスの計算式です。

  1. データの前処理

画像データは通常、ピクセル マトリックスの形式で VAE モデルに入力されるため、ピクセルを [0,1] の間の実数に正規化するか、ピクセルとそれらを標準偏差で割る 標準化など

  1. 順伝播

エンコーダを使用してデータxxを入力しますx は、潜在空間の潜在変数zzz、エンコーダの出力は平均ベクトルμ \muμと分散ベクトルσ \sigmap

μ , log σ 2 = g ϕ ( x ) \mu,log\sigma^2=g_\phi(x)メートルログσ _ _2=gφ( × )

ここで、log ⁡ σ 2 \log \sigma^2ログ_p2は、分散が正であることを確認することです。ここでは、log ⁡ σ 2 \log \sigma^2ログ_p2は、トレーニング可能なパラメーターが必要なためであり、これによりネガティブなケースが回避されます。

  1. サンプリング

事後分布q ϕ ( z ∣ x ) q_{\phi}(z|x)からqφ( z x )のサンプルzz

z = μ + ϵ ⊙ σ , ϵ N ( 0 , 1 ) z=\mu+\epsilon \odot \sigma,\epsilon ~N(0,1)=メートル+ϵ_ε N ( 0 , 1 )

ここで、ε\εϵは、標準正規分布⊙ \odotからサンプリングされたノイズ ベクトルです。⊙は要素間の乗算を意味します

  1. デコード

デコーダーを使用して潜在変数zzを変換しますz は、再構成されたデータx ‘ x’バツ

x ' = f θ ( z ) x'=f_\theta(z)バツ=( z )

  1. 再構成誤差の計算

再構成誤差を使用して、復号化器で再構成されたデータx ' x'を測定します。バツ'と生データxxxの差

CE = − ∑ i = 1 N xilogxi ' + ( 1 − xi ) log ( 1 − xi ' ) CE=-\sum_{i=1}^{N}x_ilogx'_i+(1-x_i)log(1-x '_私)CE=私は= 1Nバツx _+( 1バツ)ログ( 1 _ _バツ)
そのうち、NNN はデータの次元です。

  1. KL ダイバージェンスを計算する

事後分布q ϕ ( z ∣ x ) q_{\phi}(z|x) を計算しますqφ( z x )と事前分布p ( z ) p(z)p ( z )間の KL ダイバージェンス

KL ( q ϕ ( z ∣ x ) ∣ ∣ p ( z ) ) = − 1 2 ∑ j = 1 J ( 1 + log ( σ j 2 ) − μ j 2 − σ j 2 ) KL(q_\phi(z |x)||p(z))=-\frac{1}{2}\sum_{j=1}^J(1+log(\sigma_j^2)-\mu_j^2-\sigma_j^2)K L ( qφ( z x ) ∣∣ p ( z ))=21j = 1J( 1+ログ( p _ _j2)メートルj2pj2)

そのうち、JJJ は隠し変数zzzμ j \mu_jメートルσ j 2 \sigma_j^2pj2エンコーダのjj番目の出力j潜在変数の平均と分散。

  1. 損失関数を計算する

再構成誤差と KL 発散を組み合わせて、VAE の損失関数が得られます。

L = CE + β KL ( q ϕ ( z ∣ x ) ∣ ∣ p ( z ) ) L=CE+\beta KL(q_\phi(z∣x)∣∣p(z))L=CE+β K L ( qφ( z× )∣∣p ( z ))

其中, β \beta βは、再構成誤差と KL ダイバージェンスの重みのバランスをとるためのハイパーパラメーターです。

  1. 誤差逆伝播法

損失関数LLによるとLをモデルのパラメータϕ \phiφθ \thetaθ は逆伝播を実行し、パラメーターを更新します。

θ ← θ − α ∂ L ∂ θ \theta \leftarrow \theta-\alpha \frac{\partial L}{\partial \theta}a∂θ _∂L _

ϕ ← ϕ − α ∂ L ∂ ϕ \phi \leftarrow\phi-\alpha \frac{\partial L}{\partial \phi}φφaφ∂L _

その中で、α \alphaαは学習率です。損失関数が収束するか、事前設定された反復回数に達するまで、上記の手順を繰り返します。

オートエンコーダ コードの実装

ここに画像の説明を挿入

線形セルフエディターの実装

import torch
import torchvision
from torch import nn
from torch import optim
import torch.nn.functional as F
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.utils import save_image
from torchvision.datasets import MNIST
import os

if not os.path.exists('./vae_img'):
    os.mkdir('./vae_img')


def to_img(x):
    x = x.clamp(0, 1)
    x = x.view(x.size(0), 1, 28, 28)
    return x


num_epochs = 100
batch_size = 128
learning_rate = 1e-3

img_transform = transforms.Compose([
    transforms.ToTensor()
    # transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

dataset = MNIST('../data', transform=img_transform, download=True)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)


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))
        return torch.sigmoid(self.fc4(h3))

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


model = VAE()
if torch.cuda.is_available():
    # model.cuda()
    print('cuda is OK!')
    model = model.to('cuda')
else:
    print('cuda is NO!')

reconstruction_function = nn.MSELoss(size_average=False)
# reconstruction_function = nn.MSELoss(reduction=sum)


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)  # mse loss
    # 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


optimizer = optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(num_epochs):
    model.train()
    train_loss = 0
    for batch_idx, data in enumerate(dataloader):
        img, _ = data
        img = img.view(img.size(0), -1)
        img = Variable(img)
        if torch.cuda.is_available():
            img = img.cuda()
        optimizer.zero_grad()
        recon_batch, mu, logvar = model(img)
        loss = loss_function(recon_batch, img, mu, logvar)
        loss.backward()
        # train_loss += loss.data[0]
        train_loss += loss.item()
        optimizer.step()
        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch,
                batch_idx * len(img),
                len(dataloader.dataset), 100. * batch_idx / len(dataloader),
                # loss.data[0] / len(img)))
                loss.item() / len(img)))

    print('====> Epoch: {} Average loss: {:.4f}'.format(
        epoch, train_loss / len(dataloader.dataset)))
    if epoch % 10 == 0:
        save = to_img(recon_batch.cpu().data)
        save_image(save, './vae_img/image_{}.png'.format(epoch))

torch.save(model.state_dict(), './vae.pth')

畳み込みオートコンパイラの実装

import os
import datetime

import torch
import torchvision
from torch import nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.utils import save_image
from torchvision.datasets import MNIST


if not os.path.exists('./dc_img'):
    os.mkdir('./dc_img')


def to_img(x):
    x = 0.5 * (x + 1)
    x = x.clamp(0, 1)
    x = x.view(x.size(0), 1, 28, 28)
    return x


num_epochs = 100
batch_size = 128
learning_rate = 1e-3

img_transform = transforms.Compose([
    transforms.ToTensor(),
    # transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    transforms.Normalize([0.5], [0.5])
])

dataset = MNIST('./data', transform=img_transform)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)


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


model = autoencoder().cuda()
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate,weight_decay=1e-5)
starttime = datetime.datetime.now()

for epoch in range(num_epochs):
    for data in dataloader:
        img, label = data
        img = Variable(img).cuda()
        # ===================forward=====================
        output = model(img)
        loss = criterion(output, img)
        # ===================backward====================
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    # ===================log========================
    endtime = datetime.datetime.now()
    print('epoch [{}/{}], loss:{:.4f}, time:{:.2f}s'.format(epoch+1, num_epochs, loss.item(), (endtime-starttime).seconds))
    
    # if epoch % 10 == 0:
    pic = to_img(output.cpu().data)
    save_image(pic, './dc_img/image_{}.png'.format(epoch))

torch.save(model.state_dict(), './conv_autoencoder.pth')

変分自動コンパイラの実装

ここに画像の説明を挿入

import torch
import torchvision
from torch import nn
from torch import optim
import torch.nn.functional as F
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.utils import save_image
from torchvision.datasets import MNIST
import os
import datetime

if not os.path.exists('./vae_img'):
    os.mkdir('./vae_img')


def to_img(x):
    x = x.clamp(0, 1)
    x = x.view(x.size(0), 1, 28, 28)
    return x


num_epochs = 100
batch_size = 128
learning_rate = 1e-3

img_transform = transforms.Compose([
    transforms.ToTensor()
    # transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

dataset = MNIST('./data', transform=img_transform, download=True)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

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))
        return torch.sigmoid(self.fc4(h3))

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


strattime = datetime.datetime.now()
model = VAE()
if torch.cuda.is_available():
    # model.cuda()
    print('cuda is OK!')
    model = model.to('cuda')
else:
    print('cuda is NO!')

reconstruction_function = nn.MSELoss(size_average=False)
# reconstruction_function = nn.MSELoss(reduction=sum)


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)  # mse loss
    # 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


optimizer = optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(num_epochs):
    model.train()
    train_loss = 0
    for batch_idx, data in enumerate(dataloader):
        img, _ = data
        img = img.view(img.size(0), -1)
        img = Variable(img)
        img = (img.cuda() if torch.cuda.is_available() else img)
        optimizer.zero_grad()
        recon_batch, mu, logvar = model(img)
        loss = loss_function(recon_batch, img, mu, logvar)
        loss.backward()
        # train_loss += loss.data[0]
        train_loss += loss.item()
        optimizer.step()
        if batch_idx % 100 == 0:
            endtime = datetime.datetime.now()
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f} time:{:.2f}s'.format(
                epoch,
                batch_idx * len(img),
                len(dataloader.dataset), 
                100. * batch_idx / len(dataloader),
                loss.item() / len(img), 
                (endtime-strattime).seconds))
    print('====> Epoch: {} Average loss: {:.4f}'.format(
        epoch, train_loss / len(dataloader.dataset)))
    if epoch % 10 == 0:
        save = to_img(recon_batch.cpu().data)
        save_image(save, './vae_img/image_{}.png'.format(epoch))

torch.save(model.state_dict(), './vae.pth')

おすすめ

転載: blog.csdn.net/weixin_42010722/article/details/129969178