専門ユニット2/山内研セミナー(2023/11/21)

関連サイトと資料

6.RNN(再帰型ニューラルネットワーク)

シンプルなRNNの実装

その1

import torch
import math
import matplotlib.pyplot as plt
  
sin_x = torch.linspace(-2*math.pi, 2*math.pi, 100)  # -2πから2πまで
sin_y = torch.sin(sin_x)  + 0.1*torch.randn(len(sin_x))  # sin関数に乱数でノイズを加える
plt.plot(sin_x, sin_y)
plt.show()
    

その2

from torch.utils.data import TensorDataset, DataLoader
  
n_time = 10  # 時刻の数
n_sample = len(sin_x)-n_time  # サンプル数
  
input_data = torch.zeros((n_sample, n_time, 1))  # 入力
correct_data = torch.zeros((n_sample, 1))  # 正解
for i in range(n_sample):
    input_data[i] = sin_y[i:i+n_time].view(-1, 1)  # (時刻の数, 入力の数)
    correct_data[i] = sin_y[i+n_time:i+n_time+1]  # 正解は入力よりも1つ後
  
dataset = TensorDataset(input_data, correct_data)  # データセットの作成
train_loader = DataLoader(dataset, batch_size=8, shuffle=True)  # DataLoaderの設定
    

その3

import torch.nn as nn
  
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.rnn = nn.RNN(  # RNN層
            input_size=1,  # 入力数
            hidden_size=64,  # ニューロン数
            batch_first=True,  # 入力の形状を (バッチサイズ, 時刻の数, 入力の数) にする
        )
        self.fc = nn.Linear(64, 1)  # 全結合層
  
    def forward(self, x):
        # y_rnn:全時刻の出力 h:中間層の最終時刻の値
        y_rnn, h = self.rnn(x, None)
        y = self.fc(y_rnn[:, -1, :])  # -1で最後の時刻のみ取得して全結合層へ渡す
        return y
  
net = Net()
print(net)
    

その4

from torch import optim
  
# 平均二乗誤差
loss_fnc = nn.MSELoss()
  
# 最適化アルゴリズム
optimizer = optim.SGD(net.parameters(), lr=0.01)  # 学習率は0.01
  
# 損失のログ
record_loss_train = []
  
# 学習
epochs = 100  # エポック数
for i in range(epochs):
    net.train()  # 訓練モード
    loss_train = 0
    for j, (x, t) in enumerate(train_loader):  # ミニバッチ(x, t)を取り出す
        y = net(x)
        loss = loss_fnc(y, t)
        loss_train += loss.item()
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    loss_train /= j+1
    record_loss_train.append(loss_train)
  
    # 経過の表示
    if i%10==0 or i==epochs-1:
        net.eval()  # 評価モード
        print("Epoch:", i, "Loss_Train:", loss_train)
        predicted = list(input_data[0].view(-1)) # 最初の入力
        for i in range(n_sample):
            x = torch.tensor(predicted[-n_time:])  # 直近の時系列を取り出す
            x = x.view(1, n_time, 1)  # (バッチサイズ, 時刻の数, 入力の数)
            y = net(x)
            predicted.append(y[0].item())  # 予測結果をpredictedに追加する
  
        plt.plot(range(len(sin_y)), sin_y, label="Correct")
        plt.plot(range(len(predicted)), predicted, label="Predicted")
        plt.legend()
        plt.show()
    

その5

plt.plot(range(len(record_loss_train)), record_loss_train, label="Train")
plt.legend()
  
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()
    

RNNによる画像生成

その6

from torchvision.datasets import FashionMNIST
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
  
fmnist_data = FashionMNIST(root="./data",
                            train=True,download=True,
                            transform=transforms.ToTensor())
fmnist_classes = ["T-shirt/top", "Trouser", "Pullover", "Dress", "Coat",
                  "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot"]
print("データの数:", len(fmnist_data))
  
n_image = 25  # 表示する画像の数
fmnist_loader = DataLoader(fmnist_data, batch_size=n_image, shuffle=True)
dataiter = iter(fmnist_loader)  # イテレータ
images, labels = next(dataiter)  # 最初のバッチを取り出す
  
img_size = 28
plt.figure(figsize=(10,10))  # 画像の表示サイズ
for i in range(n_image):
    ax = plt.subplot(5,5,i+1)
    ax.imshow(images[i].view(img_size, img_size), cmap="Greys_r")
    label = fmnist_classes[labels[i]]
    ax.set_title(label)
    ax.get_xaxis().set_visible(False)  # 軸を非表示に
    ax.get_yaxis().set_visible(False)
  
plt.show()
    

その7

import torch
from torch.utils.data import TensorDataset
  
n_time = 14  # 時刻の数
n_in = img_size  # 入力層のニューロン数
n_mid = 256  # 中間層のニューロン数
n_out = img_size  # 出力層のニューロン数
n_sample_in_img = img_size - n_time  # 1枚の画像中のサンプル数
  
dataloader = DataLoader(fmnist_data, batch_size=len(fmnist_data), shuffle=False)
dataiter = iter(dataloader)  # イテレータ
train_imgs, labels = next(dataiter)  # データを取り出す
train_imgs = train_imgs.view(-1, img_size, img_size)
  
n_sample = len(train_imgs) * n_sample_in_img  # サンプル数
  
input_data = torch.zeros((n_sample, n_time, n_in))  # 入力
correct_data = torch.zeros((n_sample, n_out))  # 正解
for i in range(len(train_imgs)):
    for j in range(n_sample_in_img):
        sample_id = i*n_sample_in_img + j
        input_data[sample_id] = train_imgs[i, j:j+n_time]
        correct_data[sample_id] = train_imgs[i, j+n_time]
  
dataset = TensorDataset(input_data, correct_data)  # データセットの作成
train_loader = DataLoader(dataset, batch_size=128, shuffle=True)  # DataLoaderの設定
    

その8

n_disp = 10  # 生成し表示する画像の数
  
disp_data = FashionMNIST(root="./data",
                            train=False,download=True,
                            transform=transforms.ToTensor())
disp_loader = DataLoader(disp_data, batch_size=n_disp, shuffle=False)
dataiter = iter(disp_loader)  # イテレータ
disp_imgs, labels = next(dataiter)  # データを取り出す
disp_imgs = disp_imgs.view(-1, img_size, img_size)
    

その9

import torch.nn as nn
  
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.rnn = nn.LSTM(  # LSTM層
            input_size=n_in,  # 入力サイズ
            hidden_size=n_mid,  # ニューロン数
            batch_first=True,  # 入力を (バッチサイズ, 時刻の数, 入力の数) にする
        )
        self.fc = nn.Linear(n_mid, n_out)  # 全結合層
  
    def forward(self, x):
        # y_rnn:全時刻の出力 h:中間層の最終時刻の値 c:記憶セル
        y_rnn, (h, c) = self.rnn(x, None)
        y = self.fc(y_rnn[:, -1, :])  # yは最後の時刻の出力
        return y
  
net = Net()
net.cuda()  # GPU対応
print(net)
    

その10

def generate_images():
    # オリジナルの画像
    print("Original:")
    plt.figure(figsize=(20, 2))
    for i in range(n_disp):
        ax = plt.subplot(1, n_disp, i+1)
        ax.imshow(disp_imgs[i], cmap="Greys_r", vmin=0.0, vmax=1.0)
        ax.get_xaxis().set_visible(False)  # 軸を非表示に
        ax.get_yaxis().set_visible(False)
    plt.show()
  
    # 下半分をRNNにより生成した画像
    print("Generated:")
    net.eval()  # 評価モード
    gen_imgs = disp_imgs.clone()
    plt.figure(figsize=(20, 2))
    for i in range(n_disp):
        for j in range(n_sample_in_img):
            x = gen_imgs[i, j:j+n_time].view(1, n_time, img_size)
            x = x.cuda()  # GPU対応
            gen_imgs[i, j+n_time] = net(x)[0]
        ax = plt.subplot(1, n_disp, i+1)
        ax.imshow(gen_imgs[i].detach(), cmap="Greys_r", vmin=0.0, vmax=1.0)
        ax.get_xaxis().set_visible(False)  # 軸を非表示に
        ax.get_yaxis().set_visible(False)
    plt.show()
    

その11

from torch import optim
  
# 平均二乗誤差
loss_fnc = nn.MSELoss()
  
# 最適化アルゴリズム
optimizer = optim.Adam(net.parameters())
  
# 損失のログ
record_loss_train = []
  
# 学習
epochs = 30  # エポック数
for i in range(epochs):
    net.train()  # 訓練モード
    loss_train = 0
    for j, (x, t) in enumerate(train_loader):  # ミニバッチ(x, t)を取り出す
        x, t = x.cuda(), t.cuda()  # GPU対応
        y = net(x)
        loss = loss_fnc(y, t)
        loss_train += loss.item()
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    loss_train /= j+1
    record_loss_train.append(loss_train)
  
    if i%5==0 or i==epochs-1:
        print("Epoch:", i, "Loss_Train:", loss_train)
        generate_images()
    

その12

plt.plot(range(len(record_loss_train)), record_loss_train, label="Train")
plt.legend()
  
plt.xlabel("Epochs")
plt.ylabel("Error")
plt.show()