PyTorchで関数フィッティング その2:Batch正規化の導入

前回の記事：
「PyTorchで関数フィッティング」
https://qiita.com/cometscome_phys/items/61331dd2eedd0c846336
に引き続き、PythonでのPyTorchを試してみる。今回は、Batch Normalization （バッチ正規化）を使う。
TensorFlowでのバッチ正規化の記事は
「TensorFlowの高レベルAPIを使ったBatch Normalizationの実装」
https://qiita.com/cometscome_phys/items/6d5d3c74d7000382efef
にあるので、これと同様なことをやりたい。この記事と同じ流れでPyTorchでの実装を行う。

# バージョン
PyTorch: 1.0
Python 3.6.4

# 再現すべき関数

再現すべき関数は

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

n = 10
x0 = np.linspace(-2.0, 2.0, n)
a0 = 3.0
a1 = 2.0
b0 = 1.0
y0 = np.zeros((n,1))
y0[:,0] = a0*x0+a1*x0**2 + b0 + 3*np.cos(20*x0)

nm = 300
xmany = np.linspace(-2.0, 2.0, nm)
ymany = np.zeros((nm,1))
ymany[:,0] = a0*xmany+a1*xmany**2 + b0 + 3*np.cos(20*xmany)

plt.plot(xmany,ymany )
plt.show()
plt.savefig("graph_many.png")
```
であり、

![Unknown-4.png](https://qiita-image-store.s3.amazonaws.com/0/246113/9726d43c-d5c7-a35d-f50d-4612653f33fa.png)
である。

# ランダムバッチ：バッチ正規化なし
まずはじめに、バッチ正規化なしの場合を考える。
また、前回の記事では使わなかったランダムバッチを使ってみよう。
前回の記事では、PyTorchへ入れるテンソルは

```Python
import torch 
import torch.nn as nn 
import torch.nn.functional as F
from torch.autograd import Variable
import torch.optim as optim
import torch.utils.data 

input = torch.from_numpy(phimany)
target =torch.from_numpy(ymany)
```
のようにして、```input```と```target```を使っていた。
ランダムバッチにしたい場合には、

```python
train = torch.utils.data.TensorDataset(input,target ) #トレーニング用
train_loader = torch.utils.data.DataLoader(train, batch_size=20, shuffle=True)
test = torch.utils.data.TensorDataset(input, target) #テスト用
test_loader = torch.utils.data.DataLoader(test, batch_size=20, shuffle=True)
```
とすればよい。ここではトレーニング用とテスト用で同じ```input```と```target```を使っているが、
よりちゃんとしたければ異なるものを用意すべきである。上では、バッチサイズを20としている。

ここからミニバッチを取り出すには、

```python
input_i,target_i = next(iter(train_loader))
```
とすればよい。

モデルは前回の記事と同じものを使うので、バッチ正規化なしの全体のコードは

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline


n = 10
x0 = np.linspace(-2.0, 2.0, n)
a0 = 3.0
a1 = 2.0
b0 = 1.0
y0 = np.zeros((n,1),dtype = 'Float32')
y0[:,0] = a0*x0+a1*x0**2 + b0 + 3*np.cos(20*x0)

nm = 300
xmany = np.linspace(-2.0, 2.0, nm)
ymany = np.zeros((nm,1),dtype = 'Float32')
ymany[:,0] = a0*xmany+a1*xmany**2 + b0 + 3*np.cos(20*xmany)

plt.plot(xmany,ymany )
plt.show()
plt.savefig("graph_many.png")



def make_phi(x0,n,k):    
    phi = np.array([x0**j for j in range(k)],dtype = 'Float32')
    return phi.T

k = 6
phi = make_phi(x0,n,k)
print(phi[0,:])
phimany = make_phi(xmany,nm,k)
d_input = k
d_middle = 10

import torch 
import torch.nn as nn 
import torch.nn.functional as F
from torch.autograd import Variable
import torch.optim as optim
import torch.utils.data 




class Net(nn.Module):
    def __init__(self,d_inp,d_middle,d_out):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(d_inp,d_middle)
        self.fc2 = nn.Linear(d_middle, d_out)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)        
        return x
    
    
    
learning_rate = 0.001
d_out = 1

net = Net(d_input,d_middle,d_out)
print(net)
params = list(net.parameters())
print(params)

input = torch.from_numpy(phimany)
target =torch.from_numpy(ymany)
train = torch.utils.data.TensorDataset(input,target )
train_loader = torch.utils.data.DataLoader(train, batch_size=20, shuffle=True)
test = torch.utils.data.TensorDataset(input, target)
test_loader = torch.utils.data.DataLoader(test, batch_size=20, shuffle=True)

criterion = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=learning_rate)
nt = 2000

vec_loss = []
for i in range(nt):
    optimizer.zero_grad()
    input_i,target_i = next(iter(train_loader)) #トレーニング用ミニバッチ
    
    output_i = net(input_i)
    loss = criterion(output_i, target_i) #トレーニングのloss
    loss.backward()
    optimizer.step()
    input_test_i,target_test_i = next(iter(test_loader)) #テスト用ミニバッチ
    output_test_i = net(input_test_i)
    loss_test = criterion(output_test_i, target_test_i) #テストのloss
    vec_loss.append(loss_test)
        
    
    if i %100 is 0:        
        print(i,loss_test.item())

print(params[:])      
print('Finished Training')
ye = net(input_test).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2.png")

plt.plot(vec_loss,label = "Test data")
plt.legend() 
plt.show()
plt.savefig("loss.png")
```

となる。
得られる図は、

![Unknown-1.png](https://qiita-image-store.s3.amazonaws.com/0/246113/1d13d073-fa8a-7a59-3f2e-a87ac83f4249.png)
となる。lossは

![Unknown-2.png](https://qiita-image-store.s3.amazonaws.com/0/246113/0998c413-79d8-5cf5-8750-525e2b89ed3d.png)
のように減っていく。

# ランダムバッチ：バッチ正規化あり
PyTorchにおいてバッチ正規化を使うには、```nn.BatchNorm1d```を使えばよい。
モデルは、

```python
class Net_BN(nn.Module):
    def __init__(self,d_inp,d_middle,d_out):
        super(Net_BN, self).__init__()
        self.fc1 = nn.Linear(d_inp,d_middle)
        self.bn1 = nn.BatchNorm1d(d_middle) #バッチ正規化
        self.fc2 = nn.Linear(d_middle, d_out)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.bn1(x) #バッチ正規化を行う
        x = self.fc2(x)        
        return x    
```
となる。先ほどの```Net```の代わりにこの```Net_BN```を使えば、コードは完全に同一となる。
つまり、

```python
input = torch.from_numpy(phimany)
target =torch.from_numpy(ymany)
train = torch.utils.data.TensorDataset(input,target )
train_loader = torch.utils.data.DataLoader(train, batch_size=20, shuffle=True)
test = torch.utils.data.TensorDataset(input, target)
test_loader = torch.utils.data.DataLoader(test, batch_size=20, shuffle=True)

criterion = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=learning_rate)
nt = 2000

vec_loss_BN = []
for i in range(nt):
    optimizer.zero_grad()
    input_i,target_i = next(iter(train_loader))
    
    output_i = net(input_i)
    loss = criterion(output_i, target_i)
    loss.backward()
    optimizer.step()
    input_test_i,target_test_i = next(iter(test_loader))
    output_test_i = net(input_test_i)
    loss_test = criterion(output_test_i, target_test_i)
    vec_loss_BN.append(loss_test)    
    
    if i %100 is 0:        
        print(i,loss_test.item())

print(params[:])      
print('Finished Training')
ye = net(input_test).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2_BN.png")

plt.plot(vec_loss_BN,label = "Test data")
plt.legend() 
plt.show()
plt.savefig("loss_BN.png")

plt.yscale("log")
plt.plot(vec_loss,label = "without BN")
plt.plot(vec_loss_BN,label = "with BN")
plt.legend() 
plt.show()
plt.savefig("loss_BNlog.png")
```

でよい。
図は、
![Unknown-8.png](https://qiita-image-store.s3.amazonaws.com/0/246113/5d67a82e-b2e6-67cb-8cf4-c8d7cbf7eb88.png)

であり、lossは

![Unknown-9.png](https://qiita-image-store.s3.amazonaws.com/0/246113/2ce3adef-a149-4572-78e5-3a586ed91df6.png) 

となる。

バッチ正規化なしとありを何回か実行し、logスケールで比較してみた。その結果、

![Unknown-6.png](https://qiita-image-store.s3.amazonaws.com/0/246113/4e7fcd3c-ec8a-0776-181f-3ddda50c88e9.png)

![Unknown-7.png](https://qiita-image-store.s3.amazonaws.com/0/246113/993e85b8-c5e3-5f93-9674-7da72161fbd0.png)

となった。このケースでは、TensorFlowの時と同様にあまり効いていないかも知れない。

# 全体のコード
最後に、トレーニング部分をひとまとめにする。

```python
def train(net,train_loader,test_loader):
    learning_rate = 0.001
    criterion = nn.MSELoss()
    optimizer = optim.Adam(net.parameters(), lr=learning_rate)
    nt = 2000
    vec_loss = []
    for i in range(nt):
        optimizer.zero_grad()
        input_i,target_i = next(iter(train_loader))
    
        output_i = net(input_i)
        loss = criterion(output_i, target_i)
        loss.backward()
        optimizer.step()
        input_test_i,target_test_i = next(iter(test_loader))
        output_test_i = net(input_test_i)
        loss_test = criterion(output_test_i, target_test_i)
        vec_loss.append(loss_test)    
    
        if i %100 is 0:        
            print(i,loss_test.item())
    return net,vec_loss
```

のようにすれば、どちらが入ってきても同じようにトレーニングができる。
これを使う場合には、

```python
def train(net,train_loader,test_loader):
    learning_rate = 0.001
    criterion = nn.MSELoss()
    optimizer = optim.Adam(net.parameters(), lr=learning_rate)
    nt = 2000
    vec_loss = []
    for i in range(nt):
        optimizer.zero_grad()
        input_i,target_i = next(iter(train_loader))
    
        output_i = net(input_i)
        loss = criterion(output_i, target_i)
        loss.backward()
        optimizer.step()
        input_test_i,target_test_i = next(iter(test_loader))
        output_test_i = net(input_test_i)
        loss_test = criterion(output_test_i, target_test_i)
        vec_loss.append(loss_test)    
    
        if i %100 is 0:        
            print(i,loss_test.item())
    return net,vec_loss

net = Net(d_input,d_middle,d_out)
net,vec_loss = train(net,train_loader,test_loader)

net_BN = Net_BN(d_input,d_middle,d_out)
net_BN,vec_loss_BN = train(net_BN,train_loader,test_loader)

print(params[:])      
print('Finished Training')
ye = net(input_test).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2.png")

ye = net_BN(input_test).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2_BN.png")

plt.plot(vec_loss_BN,label = "Test data")
plt.legend() 
plt.show()
plt.savefig("loss_BN.png")

plt.yscale("log")
plt.plot(vec_loss,label = "without BN")
plt.plot(vec_loss_BN,label = "with BN")
plt.legend() 
plt.show()
plt.savefig("loss_BNlog.png")
```
とすればよい。

全体のコードは、

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline


n = 10
x0 = np.linspace(-2.0, 2.0, n)
a0 = 3.0
a1 = 2.0
b0 = 1.0
y0 = np.zeros((n,1),dtype = 'Float32')
y0[:,0] = a0*x0+a1*x0**2 + b0 + 3*np.cos(20*x0)

nm = 300
xmany = np.linspace(-2.0, 2.0, nm)
ymany = np.zeros((nm,1),dtype = 'Float32')
ymany[:,0] = a0*xmany+a1*xmany**2 + b0 + 3*np.cos(20*xmany)

plt.plot(xmany,ymany )
plt.show()
plt.savefig("graph_many.png")



def make_phi(x0,n,k):    
    phi = np.array([x0**j for j in range(k)],dtype = 'Float32')
    return phi.T

k = 6
phi = make_phi(x0,n,k)
print(phi[0,:])
phimany = make_phi(xmany,nm,k)
d_input = k
d_middle = 10

import torch 
import torch.nn as nn 
import torch.nn.functional as F
from torch.autograd import Variable
import torch.optim as optim
import torch.utils.data 





d_out = 1

input = torch.from_numpy(phimany)
target =torch.from_numpy(ymany)
train = torch.utils.data.TensorDataset(input,target )
train_loader = torch.utils.data.DataLoader(train, batch_size=20, shuffle=True)
test = torch.utils.data.TensorDataset(input, target)
test_loader = torch.utils.data.DataLoader(test, batch_size=20, shuffle=True)


def train(net,train_loader,test_loader):
    learning_rate = 0.001
    criterion = nn.MSELoss()
    optimizer = optim.Adam(net.parameters(), lr=learning_rate)
    nt = 2000
    vec_loss = []
    for i in range(nt):
        optimizer.zero_grad()
        input_i,target_i = next(iter(train_loader))
    
        output_i = net(input_i)
        loss = criterion(output_i, target_i)
        loss.backward()
        optimizer.step()
        input_test_i,target_test_i = next(iter(test_loader))
        output_test_i = net(input_test_i)
        loss_test = criterion(output_test_i, target_test_i)
        vec_loss.append(loss_test)    
    
        if i %100 is 0:        
            print(i,loss_test.item())
    return net,vec_loss

class Net(nn.Module):
    def __init__(self,d_inp,d_middle,d_out):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(d_inp,d_middle)
        self.fc2 = nn.Linear(d_middle, d_out)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)        
        return x
    


net = Net(d_input,d_middle,d_out)
net,vec_loss = train(net,train_loader,test_loader)


class Net_BN(nn.Module):
    def __init__(self,d_inp,d_middle,d_out):
        super(Net_BN, self).__init__()
        self.fc1 = nn.Linear(d_inp,d_middle)
        self.bn1 = nn.BatchNorm1d(d_middle)
        self.fc2 = nn.Linear(d_middle, d_out)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.bn1(x)
        x = self.fc2(x)        
        return x
    
    
net_BN = Net_BN(d_input,d_middle,d_out)
net_BN,vec_loss_BN = train(net_BN,train_loader,test_loader)

  
print('Finished Training')
ye = net(input).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2.png")

ye = net_BN(input).detach().numpy()
plt.plot(xmany,ymany )
plt.plot(xmany,ye,'o')
plt.show()
plt.savefig("graph_2_BN.png")

plt.plot(vec_loss,label = "Test data without BN")
plt.legend() 
plt.show()
plt.savefig("loss_BN.png")

plt.plot(vec_loss_BN,label = "Test data with BN")
plt.legend() 
plt.show()
plt.savefig("loss_BN.png")

plt.yscale("log")
plt.plot(vec_loss,label = "without BN")
plt.plot(vec_loss_BN,label = "with BN")
plt.legend() 
plt.show()
plt.savefig("loss_BNlog.png")
```
となる。