投稿日: | 最終更新日:
【GoogleColab】ニューラルネットワークの画像生成 その3【オートエンコーダ】
GoogleColabで画像を生成するニューラルネットワークのプログラムを実行第3回目です。オートエンコーダの学習を行います。
オートエンコーダの学習
前回、オートエンコーダのニューラルネットワークの画準備を行いました。今回は、実際に学習をしてみます。
前回までの作成プログラム
①PyTorchをインポートし、学習データMINISTを取得します。
→学習データ(画像)の取得
②読み込んだ学習データMINISTを表示して確認します。
→データセットの画像確認
③学習用データセットのクラスを作成します。
→学習用データセットの準備
④DataSubsetのインスタンスを生成し、画像などの設定を行います。
→DataSubsetのインスタンス準備
⑤DataLoaderの設定を行います。設定したら一度ミニバッチを表示します。
→DataLoaderの設定
⑥ニューラルネットクラスを作成します。オートエンコーダー、オートデコーダーをここで定義します。
→ニューラルネットの準備
⑦結果表示用関数を作成します。オートエンコーダー、オートデコーダーを出る前と出た後の画像両方を表示します。
→結果表示用の関数
⑧AutoEncorderとLinerAEのインスタンスを生成し、学習前のモデルの振る舞いを確認します。
→オートエンコーダーの準備
必要なもの
必要なものはWindowsあるいはMacPCのみです。ただし、GoogleChoromeをインストールしている必要があります。GoogleColabを使うには、以下のステップが必要です。
①Googleアカウントを用意してログインする。
②Colabにアクセスし、プログラムを書き込むノートブックを作成する。
実行環境の準備
①Colabにアクセスします。
②左上の「ドライブにアクセス」をクリックします。
③「その他」の「アプリを追加」から「Colaboratory」をインストールします。
④ノートブックをひらけばpythonのコードが記述できます。
オートエンコーダの学習用関数
オートエンコーダの学習を行うための関数を以下のように定義します。
def train_ae(net, loader, num_epochs=3): # 勾配降下法はAdamを使用 optimizer = torch.optim.Adam(params=net.parameters(), lr=0.001, betas=(0.9, 0.999), weight_decay=1e-5) # 損失関数 criterion = nn.MSELoss(reduction = 'mean') net.train() loss_log = [] for epoch in range(num_epochs): print(f'Epoch {epoch + 1}/{num_epochs}¥t|¥t',end='') sum_loss = 0.0 for imgs in loader: #入力を1次元にする imgs = imgs.view(-1, imgs.shape[2] * imgs.shape[3]) imgs = imgs.to(device) outputs = net(imgs) loss = criterion(outputs, imgs) optimizer.zero_grad() #勾配を初期化 loss.backward() #誤差逆伝播法 optimizer.step() #パラメータ更新 # 損失を1バッチ分加算 sum_loss += loss.item() * imgs.size(0) # 1エポック分の評価価値を計算 e_loss = sum_loss / len(loader.dataset) #エポックの平均損失 print(f"Loss : {e_loss:.4f}") loss_log.append(e_loss) return loss_log
解説
# 勾配降下法はAdamを使用 optimizer = torch.optim.Adam(params=net.parameters(), lr=0.001, betas=(0.9, 0.999), weight_decay=1e-5)
重みのパラメーター更新は、誤差逆伝播法を用いた勾配降下法で行います。Adam関数は、勾配降下法を効率よく行う手法です。
# 損失関数 criterion = nn.MSELoss(reduction = 'mean')
MES損失を誤差逆伝播法における誤差の基準として指定します。つまり、オートエンコーダの出力が、入力とできるだけ近くように学習させます。
for epoch in range(num_epochs): print(f'Epoch {epoch + 1}/{num_epochs}¥t|¥t',end='') sum_loss = 0.0 for imgs in loader: #入力を1次元にする imgs = imgs.view(-1, imgs.shape[2] * imgs.shape[3]) imgs = imgs.to(device) outputs = net(imgs) loss = criterion(outputs, imgs) optimizer.zero_grad() #勾配を初期化 loss.backward() #誤差逆伝播法 optimizer.step() #パラメータ更新 # 損失を1バッチ分加算 sum_loss += loss.item() * imgs.size(0) # 1エポック分の評価価値を計算 e_loss = sum_loss / len(loader.dataset) #エポックの平均損失 print(f"Loss : {e_loss:.4f}") loss_log.append(e_loss)
データセット全てのデータを1回ずつ入力することを「エポック」(epoch)と呼びます。このエポックを繰り返すループです。また1つのエポック内では、データセットからミニバッチを順に取り出します。これが内側のforループになります。各反復でどのデータが選ばれるかランダムになっていますが、一通り全てのデータが取り出されると、1つのエポックは終了です。
# 1エポック分の評価価値を計算 e_loss = sum_loss / len(loader.dataset) #エポックの平均損失 print(f"Loss : {e_loss:.4f}") loss_log.append(e_loss)
最後そのエポックにおける誤差(損失)の平均を計算し、表示します。
学習の実行
学習の実行を以下のように行います。
num_epochs = 100 print('=' * 10, 'Train AutoEncoder', '=' * 10) ae_loss_log = train_ae(ae_net, loader, num_epochs=num_epochs) print('=' * 10, 'Train LinerAE', '=' * 10) linae_loss_log = train_ae(linae_net, loader, num_epochs=num_epochs) #学習曲線をプロット fig = plt.figure(figsize=(8,6)) plt.plot(range(1, len(ae_loss_log) + 1), ae_loss_log, '.-', label='AE') plt.plot(range(1, len(linae_loss_log) + 1), linae_loss_log, '.-', label='Linear') plt.legend() plt.xlabel('Epoch') plt.ylabel('Loss') plt.ylim(0) plt.show() #再構成画像を確認 print('AutoEncorder') input_and_reconst(ae_net, loader, seed=0) print('Linear module') input_and_reconst(linae_net, loader, seed=0)
解説
num_epochs = 100
number_epochsでは、学習を何エポック繰り返すかを指定しています。ここでは、100とします。
print('=' * 10, 'Train AutoEncoder', '=' * 10) ae_loss_log = train_ae(ae_net, loader, num_epochs=num_epochs) print('=' * 10, 'Train LinerAE', '=' * 10) linae_loss_log = train_ae(linae_net, loader, num_epochs=num_epochs)
通常の非線形なオートエンコーダ(Autoencoder)と、線形なオートエンコーダ(LinearAE)を「traon_ae()」で学習します。
#学習曲線をプロット fig = plt.figure(figsize=(8,6)) plt.plot(range(1, len(ae_loss_log) + 1), ae_loss_log, '.-', label='AE') plt.plot(range(1, len(linae_loss_log) + 1), linae_loss_log, '.-', label='Linear') plt.legend() plt.xlabel('Epoch') plt.ylabel('Loss') plt.ylim(0) plt.show()
学習時に損失がどのように変化していったかを、横軸をエポック数にしてプロットしたものです。いずれのモデルもエポック数が大きくなるに従って損失が小さくなっていきます。
#再構成画像を確認 print('AutoEncorder') input_and_reconst(ae_net, loader, seed=0) print('Linear module') input_and_reconst(linae_net, loader, seed=0)
学習後の再構成画像を入力画像と共に確認します。学習後は、以下の下段のように、いずれの入力画像もある程度復元されているのが分かります。線形モデルの結果がぼやけているのに対し、非線形の活性化関数を用いたオートエンコーダの方は、比較的明瞭です。
========== Train AutoEncoder ========== Epoch 1/100¥t|¥tLoss : 0.0548 Epoch 2/100¥t|¥tLoss : 0.0511 Epoch 3/100¥t|¥tLoss : 0.0489 Epoch 4/100¥t|¥tLoss : 0.0473 Epoch 5/100¥t|¥tLoss : 0.0470 Epoch 6/100¥t|¥tLoss : 0.0475 Epoch 7/100¥t|¥tLoss : 0.0466 Epoch 8/100¥t|¥tLoss : 0.0467 Epoch 9/100¥t|¥tLoss : 0.0470 Epoch 10/100¥t|¥tLoss : 0.0470 Epoch 11/100¥t|¥tLoss : 0.0481 Epoch 12/100¥t|¥tLoss : 0.0483 Epoch 13/100¥t|¥tLoss : 0.0478 Epoch 14/100¥t|¥tLoss : 0.0474 Epoch 15/100¥t|¥tLoss : 0.0474 Epoch 16/100¥t|¥tLoss : 0.0464 Epoch 17/100¥t|¥tLoss : 0.0471 Epoch 18/100¥t|¥tLoss : 0.0473 Epoch 19/100¥t|¥tLoss : 0.0465 Epoch 20/100¥t|¥tLoss : 0.0462 Epoch 21/100¥t|¥tLoss : 0.0457 Epoch 22/100¥t|¥tLoss : 0.0480 Epoch 23/100¥t|¥tLoss : 0.0476 Epoch 24/100¥t|¥tLoss : 0.0473 Epoch 25/100¥t|¥tLoss : 0.0478 Epoch 26/100¥t|¥tLoss : 0.0470 Epoch 27/100¥t|¥tLoss : 0.0467 Epoch 28/100¥t|¥tLoss : 0.0457 Epoch 29/100¥t|¥tLoss : 0.0457 Epoch 30/100¥t|¥tLoss : 0.0458 Epoch 31/100¥t|¥tLoss : 0.0458 Epoch 32/100¥t|¥tLoss : 0.0461 Epoch 33/100¥t|¥tLoss : 0.0463 Epoch 34/100¥t|¥tLoss : 0.0461 Epoch 35/100¥t|¥tLoss : 0.0463 Epoch 36/100¥t|¥tLoss : 0.0466 Epoch 37/100¥t|¥tLoss : 0.0477 Epoch 38/100¥t|¥tLoss : 0.0469 Epoch 39/100¥t|¥tLoss : 0.0464 Epoch 40/100¥t|¥tLoss : 0.0459 Epoch 41/100¥t|¥tLoss : 0.0461 Epoch 42/100¥t|¥tLoss : 0.0465 Epoch 43/100¥t|¥tLoss : 0.0474 Epoch 44/100¥t|¥tLoss : 0.0489 Epoch 45/100¥t|¥tLoss : 0.0493 Epoch 46/100¥t|¥tLoss : 0.0492 Epoch 47/100¥t|¥tLoss : 0.0490 Epoch 48/100¥t|¥tLoss : 0.0473 Epoch 49/100¥t|¥tLoss : 0.0462 Epoch 50/100¥t|¥tLoss : 0.0463 Epoch 51/100¥t|¥tLoss : 0.0461 Epoch 52/100¥t|¥tLoss : 0.0460 Epoch 53/100¥t|¥tLoss : 0.0453 Epoch 54/100¥t|¥tLoss : 0.0451 Epoch 55/100¥t|¥tLoss : 0.0453 Epoch 56/100¥t|¥tLoss : 0.0448 Epoch 57/100¥t|¥tLoss : 0.0452 Epoch 58/100¥t|¥tLoss : 0.0450 Epoch 59/100¥t|¥tLoss : 0.0446 Epoch 60/100¥t|¥tLoss : 0.0448 Epoch 61/100¥t|¥tLoss : 0.0450 Epoch 62/100¥t|¥tLoss : 0.0456 Epoch 63/100¥t|¥tLoss : 0.0453 Epoch 64/100¥t|¥tLoss : 0.0450 Epoch 65/100¥t|¥tLoss : 0.0453 Epoch 66/100¥t|¥tLoss : 0.0473 Epoch 67/100¥t|¥tLoss : 0.0476 Epoch 68/100¥t|¥tLoss : 0.0475 Epoch 69/100¥t|¥tLoss : 0.0468 Epoch 70/100¥t|¥tLoss : 0.0457 Epoch 71/100¥t|¥tLoss : 0.0457 Epoch 72/100¥t|¥tLoss : 0.0452 Epoch 73/100¥t|¥tLoss : 0.0449 Epoch 74/100¥t|¥tLoss : 0.0441 Epoch 75/100¥t|¥tLoss : 0.0447 Epoch 76/100¥t|¥tLoss : 0.0448 Epoch 77/100¥t|¥tLoss : 0.0441 Epoch 78/100¥t|¥tLoss : 0.0446 Epoch 79/100¥t|¥tLoss : 0.0449 Epoch 80/100¥t|¥tLoss : 0.0450 Epoch 81/100¥t|¥tLoss : 0.0451 Epoch 82/100¥t|¥tLoss : 0.0443 Epoch 83/100¥t|¥tLoss : 0.0441 Epoch 84/100¥t|¥tLoss : 0.0437 Epoch 85/100¥t|¥tLoss : 0.0438 Epoch 86/100¥t|¥tLoss : 0.0438 Epoch 87/100¥t|¥tLoss : 0.0441 Epoch 88/100¥t|¥tLoss : 0.0437 Epoch 89/100¥t|¥tLoss : 0.0443 Epoch 90/100¥t|¥tLoss : 0.0439 Epoch 91/100¥t|¥tLoss : 0.0442 Epoch 92/100¥t|¥tLoss : 0.0449 Epoch 93/100¥t|¥tLoss : 0.0460 Epoch 94/100¥t|¥tLoss : 0.0451 Epoch 95/100¥t|¥tLoss : 0.0450 Epoch 96/100¥t|¥tLoss : 0.0461 Epoch 97/100¥t|¥tLoss : 0.0451 Epoch 98/100¥t|¥tLoss : 0.0445 Epoch 99/100¥t|¥tLoss : 0.0452 Epoch 100/100¥t|¥tLoss : 0.0451 ========== Train LinerAE ========== Epoch 1/100¥t|¥tLoss : 0.2794 Epoch 2/100¥t|¥tLoss : 0.1677 Epoch 3/100¥t|¥tLoss : 0.1490 Epoch 4/100¥t|¥tLoss : 0.1441 Epoch 5/100¥t|¥tLoss : 0.1427 Epoch 6/100¥t|¥tLoss : 0.1419 Epoch 7/100¥t|¥tLoss : 0.1415 Epoch 8/100¥t|¥tLoss : 0.1410 Epoch 9/100¥t|¥tLoss : 0.1406 Epoch 10/100¥t|¥tLoss : 0.1401 Epoch 11/100¥t|¥tLoss : 0.1397 Epoch 12/100¥t|¥tLoss : 0.1392 Epoch 13/100¥t|¥tLoss : 0.1388 Epoch 14/100¥t|¥tLoss : 0.1383 Epoch 15/100¥t|¥tLoss : 0.1379 Epoch 16/100¥t|¥tLoss : 0.1375 Epoch 17/100¥t|¥tLoss : 0.1370 Epoch 18/100¥t|¥tLoss : 0.1365 Epoch 19/100¥t|¥tLoss : 0.1361 Epoch 20/100¥t|¥tLoss : 0.1357 Epoch 21/100¥t|¥tLoss : 0.1353 Epoch 22/100¥t|¥tLoss : 0.1349 Epoch 23/100¥t|¥tLoss : 0.1345 Epoch 24/100¥t|¥tLoss : 0.1341 Epoch 25/100¥t|¥tLoss : 0.1338 Epoch 26/100¥t|¥tLoss : 0.1334 Epoch 27/100¥t|¥tLoss : 0.1331 Epoch 28/100¥t|¥tLoss : 0.1327 Epoch 29/100¥t|¥tLoss : 0.1324 Epoch 30/100¥t|¥tLoss : 0.1320 Epoch 31/100¥t|¥tLoss : 0.1317 Epoch 32/100¥t|¥tLoss : 0.1315 Epoch 33/100¥t|¥tLoss : 0.1311 Epoch 34/100¥t|¥tLoss : 0.1309 Epoch 35/100¥t|¥tLoss : 0.1306 Epoch 36/100¥t|¥tLoss : 0.1304 Epoch 37/100¥t|¥tLoss : 0.1301 Epoch 38/100¥t|¥tLoss : 0.1298 Epoch 39/100¥t|¥tLoss : 0.1297 Epoch 40/100¥t|¥tLoss : 0.1294 Epoch 41/100¥t|¥tLoss : 0.1292 Epoch 42/100¥t|¥tLoss : 0.1290 Epoch 43/100¥t|¥tLoss : 0.1288 Epoch 44/100¥t|¥tLoss : 0.1286 Epoch 45/100¥t|¥tLoss : 0.1285 Epoch 46/100¥t|¥tLoss : 0.1283 Epoch 47/100¥t|¥tLoss : 0.1281 Epoch 48/100¥t|¥tLoss : 0.1280 Epoch 49/100¥t|¥tLoss : 0.1279 Epoch 50/100¥t|¥tLoss : 0.1277 Epoch 51/100¥t|¥tLoss : 0.1276 Epoch 52/100¥t|¥tLoss : 0.1275 Epoch 53/100¥t|¥tLoss : 0.1273 Epoch 54/100¥t|¥tLoss : 0.1272 Epoch 55/100¥t|¥tLoss : 0.1271 Epoch 56/100¥t|¥tLoss : 0.1270 Epoch 57/100¥t|¥tLoss : 0.1269 Epoch 58/100¥t|¥tLoss : 0.1268 Epoch 59/100¥t|¥tLoss : 0.1267 Epoch 60/100¥t|¥tLoss : 0.1267 Epoch 61/100¥t|¥tLoss : 0.1266 Epoch 62/100¥t|¥tLoss : 0.1265 Epoch 63/100¥t|¥tLoss : 0.1264 Epoch 64/100¥t|¥tLoss : 0.1264 Epoch 65/100¥t|¥tLoss : 0.1263 Epoch 66/100¥t|¥tLoss : 0.1263 Epoch 67/100¥t|¥tLoss : 0.1262 Epoch 68/100¥t|¥tLoss : 0.1262 Epoch 69/100¥t|¥tLoss : 0.1261 Epoch 70/100¥t|¥tLoss : 0.1261 Epoch 71/100¥t|¥tLoss : 0.1261 Epoch 72/100¥t|¥tLoss : 0.1260 Epoch 73/100¥t|¥tLoss : 0.1259 Epoch 74/100¥t|¥tLoss : 0.1259 Epoch 75/100¥t|¥tLoss : 0.1259 Epoch 76/100¥t|¥tLoss : 0.1259 Epoch 77/100¥t|¥tLoss : 0.1258 Epoch 78/100¥t|¥tLoss : 0.1258 Epoch 79/100¥t|¥tLoss : 0.1258 Epoch 80/100¥t|¥tLoss : 0.1257 Epoch 81/100¥t|¥tLoss : 0.1257 Epoch 82/100¥t|¥tLoss : 0.1257 Epoch 83/100¥t|¥tLoss : 0.1257 Epoch 84/100¥t|¥tLoss : 0.1257 Epoch 85/100¥t|¥tLoss : 0.1257 Epoch 86/100¥t|¥tLoss : 0.1257 Epoch 87/100¥t|¥tLoss : 0.1256 Epoch 88/100¥t|¥tLoss : 0.1256 Epoch 89/100¥t|¥tLoss : 0.1256 Epoch 90/100¥t|¥tLoss : 0.1257 Epoch 91/100¥t|¥tLoss : 0.1256 Epoch 92/100¥t|¥tLoss : 0.1255 Epoch 93/100¥t|¥tLoss : 0.1256 Epoch 94/100¥t|¥tLoss : 0.1255 Epoch 95/100¥t|¥tLoss : 0.1255 Epoch 96/100¥t|¥tLoss : 0.1256 Epoch 97/100¥t|¥tLoss : 0.1255 Epoch 98/100¥t|¥tLoss : 0.1255 Epoch 99/100¥t|¥tLoss : 0.1255 Epoch 100/100¥t|¥tLoss : 0.1255
潜在変数からの画像の生成
潜在変数からの画像生成を行います。(本題)オートエンコーダでは、潜在変数を決めればそこからデコーダを使って画像を生成することができます。ここで用いるオートエンコーダでは潜在変数が2次元です。つまり、2つの値を指定すれば良いのですが、どのあたりの範囲で指定すれば良いのかが分かりません。そこで、まず学習用データの画像がどのような潜在変数に変換されるかを見ておきましょう。
以下のプログラムは、全てのデータセットの画像を潜在変数へ変換するための「get_latent_variables()」という関数を定義します。
●データセットの画像を潜在変数へ変換
def get_latent_variables(net, loader): """" エンコーダで変換後の潜在変数を取得""" z_list = [] net.eval() #モデルを評価モードへ with torch.no_grad(): #勾配計算を止める for imgs in loader: #ミニバッチを入力 imgs = imgs.view( # 画像を1次元化 -1, imgs.shape[2] * imgs.shape[3] ).to(device) z_batch = net.encoder(imgs) z_list.append(z_batch.detach().cpu().numpy()) return np.concatenate(z_list, axis=0) z_encoded = get_latent_variables(ae_net, loader) print(z_encoded.shape) print(z_encoded[:10, :])
(結果)
(600, 2) [[ 5.9267254 2.1248462 ] [-0.88702893 2.7848637 ] [ 1.785806 0.42160788] [-0.28692353 -1.370374 ] [-1.2406417 4.505816 ] [ 2.3206477 -0.7260132 ] [-0.88784707 -1.036326 ] [ 1.8184772 0.4223911 ] [ 2.8289511 0.6041493 ] [-0.55544984 7.012882 ]]
解説
def get_latent_variables(net, loader): """" エンコーダで変換後の潜在変数を取得""" z_list = [] net.eval() #モデルを評価モードへ with torch.no_grad(): #勾配計算を止める for imgs in loader: #ミニバッチを入力 imgs = imgs.view( # 画像を1次元化 -1, imgs.shape[2] * imgs.shape[3] ).to(device) z_batch = net.encoder(imgs) z_list.append(z_batch.detach().cpu().numpy())
「net.encoder()」は特に重要です。オートエンコーダのうちのエンコーダの部分のみを使って各画像の存在変数へ変換しています。
z_encoded = get_latent_variables(ae_net, loader) print(z_encoded.shape) print(z_encoded[:10, :])
冒頭で定義した関数を使って潜在変数をを求めます。これによって2次元の潜在変数が600個得られます。つまり、z_encodedは2次元配列になります。
次回
ランダムに選んだ点から画像を生成します。
- Python 114
- 制作 54
- RaspberryPi 41
- Django 40
- WordPress 40
- Linux 27
- VPS 22
- JavaScript 21
- PHP 20
- HTML・CSS 19
- AWS 16
- 仮想環境 15
- レスポンシブデザイン 13
- マイコン 11
- WEB全般 11
- 動画製作 9
- Webサービス 8
- 統合開発環境 8
- 機械学習 8
- PyCharm 7
- jQuery 7
- AfterEffects 7
- 起業・設立 7
- Django REST framework 6
- C# 6
- デザイン 6
- SEO 6
- pydata 6
- Visual Studio 5
- 数学 5
- 携帯サイト 5
- heroku 5
- Mac 5
- illustrator 5
- node.js 5
- Anaconda 5
- Nginx 4
- Jupyter Notebook 4
- インフラ 4
- Google Colaboratory 4
- symfony 4
- Webスクレイピング 3
- photoshop 3
- Go言語 3
- PC 3
- ツール 3
- Docker 3
- facebook 3
- 作業効率化 3
- データベース 3
- Cloud9 3
- コマンド 2
- micro:bit 2
- Kali Linux 2
- Webサーバー 2
- MariaDB 2
- ドローン 2
- コンテナ 2
- DaVinci Resolve 2
- ネットワーク 2
- Java 2
- movie 2
- PCDJ 2
- 音楽 2
- XSERVER 2
- Ansible 1
- Vue.js 1
- JSON 1
- Bootstrap 1
- バージョン管理システム 1
- SSL 1
- S3 1
- ムームードメイン 1
- ネットワーク 1
- アニメーション 1
- D3.js 1
- Rhino 1
- アニメ 1
- git 1
- windows 1
- アクセス解析 1
- スマートフォン 1
- アフィリエイトノウハウ 1
- 知識 1
- TypeScript 1
- 役立つ本・書籍 1
- データサイエンス 1
- ESP32 1
- AI 1
- ownCloud 1
- API 1