ホーム >  機械学習 > Google Colaboratory >  【GoogleColab】ニューラルネットワークの画像生成 その2【オートエンコーダ】

投稿日:

【GoogleColab】ニューラルネットワークの画像生成 その2【オートエンコーダ】

Google ColaboratoryPython機械学習

前回に続き、GoogleColabで画像を生成するニューラルネットワークのプログラムを実行します。画像データセットのインスタンスを生成して画像変換を行います。

オートエンコーダーの画像生成のデータセット完了

前回、オートエンコーダーで画像生成をする前準備として、学習データ(画像)の取得・準備を行いました。

前回までの作成プログラム

①PyTorchをインポートし、学習データMINISTを取得します。
学習データ(画像)の取得

②読み込んだ学習データMINISTを表示して確認します。
データセットの画像確認

③学習用データセットのクラスを作成します。
学習用データセットの準備

必要なもの

必要なものはWindowsあるいはMacPCのみです。ただし、GoogleChoromeをインストールしている必要があります。GoogleColabを使うには、以下のステップが必要です。

①Googleアカウントを用意してログインする。

②Colabにアクセスし、プログラムを書き込むノートブックを作成する。

【無料】Google Colabを使ってサクッとPythonを使う【Python】

実行環境の準備

①Colabにアクセスします。

Colab

②左上の「ドライブにアクセス」をクリックします。

③「その他」の「アプリを追加」から「Colaboratory」をインストールします。

④ノートブックをひらけばpythonのコードが記述できます。


DataSubsetのインスタンス準備

DataSubsetのインスタンスを生成し、画像などの設定を行います。

IMG_MEAN, IMG_STD = (0.5, 0.5)

trans = transforms.Compose([
  transforms.Resize(64),
  transforms.ToTensor(),
  transforms.Normalize((IMG_MEAN,),(IMG_STD,)) 
])

use_classes = [1,6,9]
for c in use_classes:
  print(fmnist.classes)

# FashionMNISTのラベルなしサブセットを用意
dataset = DataSubset(orig_dataset=fmnist,
  n_each = 200, #各クラス何枚用いるか
  use_classes = [1,6,9], #加えるクラス
  transform = trans #画像読み込み変換
)
print('dataset size:',  len(dataset))

出力結果:

Trouser
Shirt
Ankle boot
dataset size: 600

解説

trans = transforms.Compose([
  transforms.Resize(64),
  transforms.ToTensor(),
  transforms.Normalize((IMG_MEAN,),(IMG_STD,)) 
])

一連の画像処理を設定します。元は解像度が28×28ピクセルの画像ですが、「transforms.Resize(64)」で64×64に拡大しています。

# FashionMNISTのラベルなしサブセットを用意
dataset = DataSubset(orig_dataset=fmnist,
  n_each = 200, #各クラス何枚用いるか
  use_classes = [1,6,9], #加えるクラス
  transform = trans #画像読み込み変換
)

DataSubsetのインスタンスを生成します。DataSubsetクラスの「__init()__」にまず値を渡します。

orig_datasetで元のデータを渡しています。

n_eachで指定した枚数だけ取り出します。

use_classesで、「shirt」(シャツ)「Trouser」(ズボン)「Ankleboot」(ブーツ)の3クラスを指定します。(元々は10クラスあります。)

transformに、読み込みたい画像処理を指定します。


DataLoaderの設定

ニューラルネットワークの学習では、画像を入力しながらパラメーターを更新して学習を進めます。実際は1枚ずつ画像を入力するのではなく、ある程度の枚数をまとめて入力します。この画像集合体の1つの単位を「ミニバッチ」と呼びます。その枚数を「バッチサイズ」

batch_size = 32 #バッチサイズ
loader = torch.utils.data.DataLoader(
    dataset, batch_size = batch_size, shuffle=True)

def tensor_imshow(img, title = None, show=True):
  """Tensorを画像として表示"""
  img = img.numpy().transpose((1,2,0))
  img = IMG_STD * img + IMG_MEAN
  plt.imshow(np.clip(img, 0, 1))
  if title is not None: plt.title(title)
  if show: plt.show()

#ミニバッチの表示
tensor_imshow(torchvision.utils.make_grid(
    next(iter(loader))
))

表示結果:

解説

batch_size = 32 #バッチサイズ
loader = torch.utils.data.DataLoader(
    dataset, batch_size = batch_size, shuffle=True)

データからミニバッチを読み込むためのクラスです。

  • 第一引数・・・どのデータから読み込むかを指定します。
  • 第二引数・・・バッチサイズを指定します。ここでは32を指定します。一度にbatch_size分だけ読み込みます。
  • 第三引数・・・この引数をTrueにすると、データセット順に読み込むのではなく、ランダムに読み込みます。
def tensor_imshow(img, title = None, show=True):
  """Tensorを画像として表示"""
  img = img.numpy().transpose((1,2,0))
  img = IMG_STD * img + IMG_MEAN
  plt.imshow(np.clip(img, 0, 1))
  if title is not None: plt.title(title)
  if show: plt.show()

ミニバッチを1回読み込んで、画像を表示させてみます。TensorをNumpyのarrayに変換し、さらに画像として表示するための関数ternsor_imshow()を用意しておきます。

#ミニバッチの表示
tensor_imshow(torchvision.utils.make_grid(
    next(iter(loader))
))

「next(iter(loader))」は、loaderをいったんイテレーターにします。その上でミニバッチを1つ読み込んでいます。これは32枚の画像ですので、torchvision.utils.make_grid()を用いでグリッド状に並べます。


ニューラルネットの準備

入力層から潜在変数へ変換するためのネットワークを「エンコーダー」と呼び、潜在変数から出力層までのネットワークを「デコーダー」と呼びます。

class Autoencoder(nn.Module):
  """"エンコーダー、デコーダー共に3層"""
  def __init__(self,img_size, nz):
    super(Autoencoder, self).__init__()
    
    input_size = img_size * img_size
    nz1 = input_size // 4
    nz2 = nz1 // 4

    self.encorder = nn.Sequential(
        nn.Linear(input_size, nz1),
        nn.ReLU(inplasce=True),
        nn.Linear(nz1,nz2),
        nn.ReLU(inplasce=True),
        nn.Linear(nz2,nz),
    )

    self.dencorder = nn.Sequential(
        nn.Linear(nz, nz2),
        nn.ReLU(inplasce=True),
        nn.Linear(nz2,nz1),
        nn.ReLU(inplasce=True),
        nn.Linear(nz1,input_size),
    )
    
    def forward(self, x):
      z=self.encoder(x)
      x=self.decoder(z)
      return x

class LinearAE(nn.Module):
  """エンコーダー、デコーダー共に1層"""
  def __init__(self,img_size, nz):
    super(LinearAE, self).__init__()
    self.encorder = nn.Linear(img_size * img_size, nz)
    self.dencorder = nn.Linear(nz, img_size * img_size)

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

解説

    input_size = img_size * img_size
    nz1 = input_size // 4
    nz2 = nz1 // 4

入力層および中間層の次元を設定しています。入力層は、64×64ピクセルの画像を1次元にしているため、4096次元になります。1つ目の中間層はその1/4、その次の中間層はさらに1/4とします。真ん中の中間層は、引数をnzで指定した次元数とします(後でnz=2)。

    self.encorder = nn.Sequential(
        nn.Linear(input_size, nz1),
        nn.ReLU(inplasce=True),
        nn.Linear(nz1,nz2),
        nn.ReLU(inplasce=True),
        nn.Linear(nz2,nz),
    )

エンコーダーの定義です。

    self.dencorder = nn.Sequential(
        nn.Linear(nz, nz2),
        nn.ReLU(inplasce=True),
        nn.Linear(nz2,nz1),
        nn.ReLU(inplasce=True),
        nn.Linear(nz1,input_size),
    )

デコーダーの定義です。

class LinearAE(nn.Module):
  """エンコーダー、デコーダー共に1層"""
  def __init__(self,img_size, nz):
    super(LinearAE, self).__init__()
    self.encorder = nn.Linear(img_size * img_size, nz)
    self.dencorder = nn.Linear(nz, img_size * img_size)

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

性能比較のため、エンコーダーとデコーダーを共に線形としたオートエンコーダをLinearAEとして定義しています。層の間に非線形な活性関数を挟まない場合、多層にしても表現能力は向上しません。そのため、ここではエンコーダーデコーダー共に1層です。


結果表示用の関数

def input_add_reconst(net, loader, seed = None):
  """入力画像と再構成画像をミニバッチ分表示"""
  if seed is not None:
    torch.manual_seed(seed)

    net.eval() #推論モード
    with torch.no_grad(): #推論のみ(勾配計算なし)
      imgs = next(iter(loader)) #DataLoaderからミニバッチ抽出
      imgs_in = imgs.view(-1, imgs.shape[2]*imgs.shape[3])
      outputs = net(imgs_in.to(device)) #順伝播による推論

      #入出力それぞれのグリッド画像を生成
      grid_i = torchvision.utils.make_grid(imgs)
      grid_o = torchvision.utils.make_grid(
          outputs.view(-1, 1, imgs.shape[2], imgs.shape[3]). \
            detach().cpu()
      )
      #入力画像と再構成画像をミニバッチ単位で表示
      tensor_imshow(grid_i, title = 'Inputs' )
      tensor_imshow(grid_o, title = 'Reconstructed' )

解説

結果表示用の関数input_and_reconst()を用意しておきます。この関数では、引数で与えたオートエンコーダーをを使ってloaderのミニバッチをそれぞれエンコードし、再びデコードします。これによって再構成された画像を、入力画像と共に表示します。

元の画像をもとに戻す場合は、「生成」よりむしろ「再構成(復元)」(reconstruction)と呼びます。


次回

工事中。


トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告