pythonでGANの勉強1 オートエンコーダ

pythonでGANの勉強をしていきたいと思います。

自分の勉強のメモとなります。
コードが見づらかったり、正しくない場合があるかもしれません。

まずは、オートエンコーダについて勉強していきます。
「はじめてのディープラーニング」をもとにkerasやpytorchでも実装してみるという流れとなります。

### オートエンコーダ
オートエンコーダはエンコーダとデコーダの2つの部分からなります。
1. エンコーダ
　訓練済みのエンコーダを使って、最初のデータ表現、例えば画像$x$を入力とすれば、その次元を$\tilde{y}$ を$\tilde{z}$ に減らす  
2. 潜在空間($z$)
　ネットワークを訓練するにあたって、潜在空間に何らかの意味が形成される
　通常は入力より小さな次元で、中間ステップとして動作
3. デコーダ
　元の表現と同じものを、元の次元で再構築する  
　このステップにより$z$は$x^*$に変換される  

オートエンコーダの訓練は以下のように行われる。
1. 画像$x$を取り出しオートエンコーダに入力する
2. $x^\*$ が出力される。これは再構成された画像である。
3. 再構成誤差を計算する。これは$x$と$x^\*$の差である。
　これは$x$と$x^\*$ の距離(例えばMAE)として計算され、明示的な目的関数($|x-x^\*|$)が定義され、勾配降下法により最適化可能となる

#### 実装
まずはnumpyだけでの実装です。
一番下にのせた参考書をもとに作成します。

まず、各層の実装です。
中間層と出力層のみとなります。

```python
import numpy as np

class BaseLayer:
    def update(self, eta):
        self.w -= eta * self.grad_w
        self.b -= eta * self.grad_b

class MiddleLayer(BaseLayer):
    def __init__(self, n_upper, n):
        self.w = np.random.randn(n_upper, n) * np.sqrt(2/n_upper)
        self.b = np.zeros(n)
    
    def forward(self, x):
        self.x = x
        self.u = np.dot(x, self.w) + self.b
        self.y = np.where(self.u <= 0, 0, self.u) # ReLU
    
    def backward(self,grad_y):
        delta = grad_y * np.where(self.u <=0 , 0, 1)
        
        self.grad_w = np.dot(self.x.T, delta)
        self.grad_b = np.sum(delta, axis=0)
        self.grad_x = np.dot(delta, self.w.T)

class OutputLayer(BaseLayer):
    def __init__(self, n_upper, n):
        self.w = np.random.randn(n_upper, n) * np.sqrt(2/n_upper)
        self.b = np.zeros(n)
    
    def forward(self, x):
        self.x = x
        u = np.dot(x, self.w) + self.b
        self.y = 1/(1+np.exp(-u))
    
    def backward(self, t):
        delta = (self.y-t) * self.y * (1-self.y)
        
        self.grad_w = np.dot(self.x.T, delta)
        self.grad_b = np.sum(delta, axis=0)
        self.grad_x = np.dot(delta, self.w.T)
```

次に、順伝搬と逆伝播、パラメータ更新の定義をします。

```python
def forward_propagation(x_mb):
    middle_layer.forward(x_mb)
    output_layer.forward(middle_layer.y)

def backpropagation(t_mb):
    output_layer.backward(t_mb)
    middle_layer.backward(output_layer.grad_x)

def update_params():
    output_layer.update(eta)
    middle_layer.update(eta)
```

学習を行います。

```python
import matplotlib.pyplot as plt
from sklearn import datasets

digits_data = datasets.load_digits()
x_train = np.asarray(digits_data.data)
x_train /= 15

def get_error(y, t):
    return 1.0/2.0*np.sum(np.square(y - t))

img_size = 8
n_in_out = img_size * img_size
n_mid = 16

eta = 0.01
epochs = 41
batch_size = 32
interval = 4

middle_layer = MiddleLayer(n_in_out, n_mid)
output_layer = OutputLayer(n_mid, n_in_out)

error_record = []
n_batch = len(x_train) // batch_size
for i in range(epochs):
    
    index_random = np.arange(len(x_train))
    np.random.shuffle(index_random)
    for j in range(n_batch):
        mb_index = index_random[j*batch_size : (j+1)*batch_size]
        x_mb = x_train[mb_index, :]
        
        forward_propagation(x_mb)
        backpropagation(x_mb)
        
        update_params()
    
    forward_propagation(x_train)
    error = get_error(output_layer.y, x_train)
    error_record.append(error)
    
    if i%interval == 0:
        print("Epoch:"+str(i+1)+'/'+str(epochs), "Error:"+str(error))

plt.plot(range(1, len(error_record)+1), error_record)
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/26d9f02a-71d3-a438-875f-39b6fafa1611.png)

結果の確認をします。
上の行は入力画像、真ん中の行は潜在空間、一番下の行が出力結果です。

```python
n_img = 10
middle_layer.forward(x_train[:n_img])
output_layer.forward(middle_layer.y)

plt.figure(figsize=(10, 3))
for i in range(n_img):
    
    ax = plt.subplot(3, n_img, i+1)
    plt.imshow(x_train[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+n_img)
    plt.imshow(middle_layer.y[i].reshape(4, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+2*n_img)
    plt.imshow(output_layer.y[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/b88d3ec6-ae4b-41b9-daed-093ce6f705d7.png)

入力画像が再現され散ることがわかります。

#### pytorch
同じことをpytorchで実装してみたいと思います。

まず必要なライブラリのインポートをします。

```python
import torch
import torch.nn as nn
import torch.optim as optimizers
from torch.utils.data import Dataset, DataLoader, TensorDataset
```

データの準備ですが、さきほどと同じデータを使用します。

```python
digits_data = datasets.load_digits()
x_train = np.asarray(digits_data.data)
x_train /= 15
x_train = x_train.reshape(-1, 1, 64)
y = digits_data.target

x_train = torch.tensor(x_train, dtype=torch.float32)
y_train = torch.tensor(y, dtype=torch.float64)

train = TensorDataset(x_train, y_train)
train_dataloader = DataLoader(train, batch_size=32, shuffle=True)
```

オートエンコーダを定義します。

```python
class AutoEncoder(nn.Module):
    def __init__(self, device='cpu'):
        super().__init__()
        self.device = device
        self.l1 = nn.Linear(64, 16)
        self.l2 = nn.Linear(16, 64)
    
    def forward(self, x):
        h = self.l1(x)
        h = torch.relu(h)
        h = self.l2(h)
        y = torch.sigmoid(h)
        
        return y
```

モデルの設定を行います。

```python
device = None

model = AutoEncoder(device=device).to(device)
criterion = nn.BCELoss()
optimizer = optimizers.Adam(model.parameters())
```

学習を実行します。

```python
epochs = 40
train_loss_record = []
for epoch in range(epochs):
    train_loss = 0.
    for (x, _) in train_dataloader:
        x = x.to(device)
        model.train()
        preds = model(x)
        loss = criterion(preds, x)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_loss += loss.item()
    
    train_loss /= len(train_dataloader)
    train_loss_record.append(train_loss)
    
    if epoch%4 == 0:
        print("Epoch: {}, Loss: {:3f}".format(epoch+1, train_loss))
```

学習の様子を可視化します。

```python
plt.plot(range(1, len(train_loss_record)+1), train_loss_record)
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/8f29ec65-6033-43b3-f8eb-f8009988e5cc.png)

入力画像と出力画像の比較を行います。

```python
n_img = 10

x = x_train[:n_img]
mid_out = model.l1(x)
out = model(x)

plt.figure(figsize=(10, 3))
for i in range(n_img):
    
    ax = plt.subplot(3, n_img, i+1)
    plt.imshow(x_train[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+n_img)
    plt.imshow(mid_out[i].reshape(4, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+2*n_img)
    plt.imshow(out[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/3df609ce-8bcd-21e6-8b04-78ee213df1be.png)

#### keras
kerasで同じものを実装します。

まず必要なライブラリのインポートをします。

```python
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Activation, Dense
```

データは他のものと同様のものもを使用します。

```python
digits_data = datasets.load_digits()
x_train = np.asarray(digits_data.data)
x_train /= 15
y = digits_data.target
```

モデルの作成をします。Sequential()を使用します。

```python
input_shape=x_train.shape[1:]
model = Sequential()
model.add(Dense(16, activation='relu', input_shape=input_shape, name='fc1'))
model.add(Dense(64, activation='softmax', name='fc2'))
```

モデルをコンパイルします。

```python
model.compile(optimizer='adam',
              loss='binary_crossentropy')
```

学習を実行します。

```python
history = model.fit(x_train, x_train,
                    epochs=100,
                    batch_size=32,
                    shuffle=True,
                    validation_data=(x_train, x_train))
```

学習の様子を可視化します。

```python
plt.plot(range(1, len(history.history['loss'])+1), history.history['loss'])
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/fea023a7-bae0-f27a-8fc1-de428a33aab6.png)

入力画像と出力画像を比較します。

```python
n_img = 10

x = np.float64(x_train[:n_img])
mid_out = np.array(model.layers[0](x))
out = np.array(model(x))

plt.figure(figsize=(10, 3))
for i in range(n_img):
    
    ax = plt.subplot(3, n_img, i+1)
    plt.imshow(x_train[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+n_img)
    plt.imshow(mid_out[i].reshape(4, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    
    ax = plt.subplot(3, n_img, i+1+2*n_img)
    plt.imshow(out[i].reshape(img_size, -1).tolist(), cmap="Greys_r")
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358656/3fa1bf59-d2f0-963a-b3f3-fbb64d8ed642.png)

参考文献

https://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AE%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B02-Python%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E5%86%8D%E5%B8%B0%E5%9E%8B%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF-VAE-Machine-Learning/dp/4815605580/ref=asc_df_4815605580/?tag=jpgo-22&linkCode=df0&hvadid=426119125892&hvpos=&hvnetw=g&hvrand=5167055507894845919&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009332&hvtargid=pla-893217600639&psc=1&th=1&psc=1

https://www.amazon.co.jp/GAN%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E5%AE%9F%E8%A3%85%E3%83%8F%E3%83%B3%E3%83%89%E3%83%96%E3%83%83%E3%82%AF-%E6%AF%9B%E5%88%A9%E6%8B%93%E4%B9%9F/dp/4798062294/ref=sr_1_1_sspa?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=38P0EZOHESZRB&keywords=GAN&qid=1642491369&s=books&sprefix=gan%2Cstripbooks%2C188&sr=1-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEySkkyOTMwN0RKQkcyJmVuY3J5cHRlZElkPUEwNDE5MjU0MzUzTDBMNzA5TjhTMSZlbmNyeXB0ZWRBZElkPUExN1g4WDlIWFhBVk9MJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5GAN-%E6%95%B5%E5%AF%BE%E7%9A%84%E7%94%9F%E6%88%90%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AB%E3%82%88%E3%82%8B%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92-Compass-Books%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Jakub/dp/4839967717/ref=pd_vtp_3/357-1227394-8103025?pd_rd_w=eQ7sv&pf_rd_p=949e26f5-c2ef-4c96-bfde-49d7614d0317&pf_rd_r=8CHSHR2J1S38CYZV0B3X&pd_rd_r=f717c836-018d-4815-ae50-c7cfcb3fff7b&pd_rd_wg=EeCAk&pd_rd_i=4839967717&psc=1