# Pytorch深度學習框架X NVIDIA JetsonNano應用-cDCGAN生成手寫數字 (繁體)

CAVE Education
 作者 張嘉鈞 難度 中偏難 材料表 Jetson Nano PC or Notebook

## 將標籤合併於資料中

 潛層合併，先合併再輸入網路 深層合併：各別輸入後再合併

OneHot編碼主要在於讓標籤離散，如果將標籤都用阿拉伯數字表示，對於神經網路而言他們屬於連續性的數值或許會將前後順序、距離給考慮進去，但是用onehot之後將可以將各類標籤單獨隔開並且對於彼此的距離也會相同。

``````    def __init__(self, z_dim, label_dim):
super(Generator, self).__init__()
self.input_x = nn.Sequential(
# input is Z, going into a convolution
nn.ConvTranspose2d(z_dim, 256, 4, 1, 0, bias=False),
nn.BatchNorm2d(256),
nn.ReLU(True),
# image size =  (1-1)*1 - 2*0 + 4 = 4
)
self.input_y = nn.Sequential(
# input is Z, going into a convolution
nn.ConvTranspose2d( label_dim, 256, 4, 1, 0, bias=False),
nn.BatchNorm2d(256),
nn.ReLU(True),
# image size =  (1-1)*1 - 2*0 + 4 = 4
)
self.concat = nn.Sequential(

# 因為 x 跟 y 水平合併所以要再乘以 2
nn.ConvTranspose2d(256*2, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(True),
# image size =  (4-1)*2 - 2*1 + 4 = 8

nn.ConvTranspose2d( 128, 64, 4, 2, 1, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(True),
# image size =  (8-1)*2 - 2*1 + 4 = 16

nn.ConvTranspose2d( 64, 1, 4, 2, 3, bias=False),
nn.Tanh()
# image size =  (16-1)*2 - 2*3 + 4 = 28
)
``````

``````    def forward(self, x, y):
x = self.input_x(x)
y = self.input_y(y)
out = torch.cat([x, y] , dim=1)
out = self.concat(out)
return out
``````

``````def print_div(text):

div='\n'
for i in range(60): div += "="
div+='\n'
print("{} {:^60} {}".format(div, text, div))

""" Define Generator """
G = Generator(100, 10)

""" Use Print"""
print_div('Print Model Directly')
print(G)

""" Use Torchsummary """
print_div('Print Model With Torchsummary')
test_x = (100, 1, 1)
test_y = (10, 1, 1)
summary(G, [test_x, test_y], batch_size=64)
``````

Tensorboard 是Google 出的強大視覺化工具，一般的文字、數值、影像、聲音都可以動態的紀錄在上面，一開始只支援Tensorflow 但是 PyTorch 1.2 之後都包含在其中 ( 但是要使用的話還是要先安裝tensorboard ) ，你可以直接從 torch.utils.tensorboard 中呼叫 Tensorboard，首先需要先實體化 SummaryWritter，接著直接使用add_graph即可將圖片存到伺服器上

``````""" Initial Parameters"""
batch_size = 1
test_x = torch.rand(batch_size, 100, 1, 1)
test_y = torch.rand(batch_size, 10, 1, 1)

print_div('Print Model With Tensorboard')
print('open terminal and input "tensorboard --logdir=runs"')
print('open browser and key http://localhost:6006')
writer = SummaryWriter()
writer.close()
``````

``> tensorboard –logdir=./runs``

``````> pip install hiddenlayer
> Pip install graphviz
``````

``\$ sudo apt-get install graphviz``

``````""" Initial Parameters"""
batch_size = 1
test_x = torch.rand(batch_size, 100, 1, 1)
test_y = torch.rand(batch_size, 10, 1, 1)

print_div('Print Model With HiddenLayer')
g_graph = hl.build_graph(G, (test_x, test_y))
g_graph.save('./images/G_grpah', format="jpg")
g_graph
``````

``````import torch
import torch.nn as nn
from torchsummary import summary

class Discriminator(nn.Module):

def __init__(self, c_dim=1, label_dim=10):

super(Discriminator, self).__init__()

self.input_x = nn.Sequential(

# Input size = 1 ,28 , 28
nn.Conv2d(c_dim, 64, (4,4), 2, 1),
nn.LeakyReLU(),
)
self.input_y = nn.Sequential(

# Input size = 10 ,28 , 28
nn.Conv2d(label_dim, 64, (4,4), 2, 1),
nn.LeakyReLU(),
)

self.concate = nn.Sequential(

# Input size = 64+64 ,14 , 14
nn.Conv2d(64*2 , 64, (4,4), 2, 1),
nn.LeakyReLU(),

# Input size = (14-4+2)/2 +1 = 7
nn.Conv2d(64, 128, 3, 2, 1),
nn.LeakyReLU(),

# Input size = (7-3+2)/2 +1 = 4
nn.Conv2d(128, 1, 4, 2, 0),
nn.Sigmoid(),

# output size = (4-4)/2 +1 = 1
)

def forward(self, x, y):

x = self.input_x(x)
y = self.input_y(y)
out = torch.cat([x, y] , dim=1)
out = self.concate(out)
return out

D = Discriminator(1, 10)
test_x = torch.rand(64, 1,28,28)
test_y = torch.rand(64, 10,28,28)

writer = SummaryWriter()
writer.close()

hl.build_graph(D, (test_x, test_y))
``````

1. 宣告固定的雜訊跟標籤用來預測用
2. 將標籤轉換成onehot格式 ( scatter )

Onehot數據處理，在torch中可以直接使用scatter的方式，我在程式註解的地方有推薦一篇文章大家可以去了解scatter的概念，至於這邊我先附上實驗的程式碼：

``````""" OneHot 格式 之 scatter 應用"""
""" 超好理解的圖形化教學 https://medium.com/@yang6367/understand-torch-scatter-b0fd6275331c """

label =torch.tensor([1,5,6,9])
print(label, label.shape)

a = torch.zeros(10).scatter_(0, label, 1)
print(a)

print('\n\n')
label_=label.unsqueeze(1)
print(label_, label_.shape)
b = torch.zeros(4,10).scatter_(1, label_, 1)
print(b)
``````

``````""" 產生固定資料，每個類別10張圖(雜訊) 以及 對應的標籤，用於視覺化結果 """
temp_noise = torch.randn(label_dim, z_dim)                   # (10, 100) 10張圖
fixed_noise = temp_noise
fixed_c = torch.zeros(label_dim, 1)                          # (10, 1 ) 10個標籤

for i in range(9):
fixed_noise = torch.cat([fixed_noise, temp_noise], 0)    #將每個類別的十張雜訊依序合併，維度1會自動boardcast
temp = torch.ones(label_dim, 1) + i                      #依序將標籤對應上 0~9
fixed_c = torch.cat([fixed_c, temp], 0)                  #將標籤也依序合併

fixed_noise = fixed_noise.view(-1, z_dim, 1, 1)              #由於是捲積所以我們要將形狀轉換成二維的
print('Predict Noise: ', fixed_noise.shape)
print('Predict Label (before): ', fixed_c.shape, '\t\t\t', fixed_c[50])

""" 針對 lael 做 onehot """
fixed_label = torch.zeros(100, label_dim)                    #先產生 [100,10] 的全0張量，100個標籤，每個標籤維度是 10
fixed_label.scatter_(1, fixed_c.type(torch.LongTensor), 1)   #轉成 onehot編碼 (1, ) -> (10, )
fixed_label = fixed_label.view(-1, label_dim, 1, 1)          #轉換形狀 (10, 1, 1 )
print('Predict Label (onehot): ',fixed_label.shape, '\t\t', fixed_label[50].view(1,-1), '\n')
``````

``````""" 幫標籤做前處理，onehot for g, fill for d """
onehot = torch.zeros(label_dim, label_dim)                   # 產生 (10,10) 10個標籤，維度為10 (onehot)
onehot = onehot.scatter_(1, torch.LongTensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).view(label_dim, 1), 1).view(label_dim, label_dim, 1, 1)
print('Train G label:',onehot[1].shape, '\n', onehot[1], '\n')                # 假設我們要取得標籤 1 的 onehot (10,1,1)，直接輸入索引 1

fill = torch.zeros([label_dim, label_dim, image_size, image_size])    # 產生 (10, 10, 28, 28) 意即 10個標籤 維度都是 (10,28,28)
for i in range(label_dim):
fill[i, i, :, :] = 1                                     # 舉例 標籤 5，第一個[]代表標籤5，第二個[]代表onehot為1的位置
print('Train D Label: ', fill.shape)
print('\n', fill[1].shape, '\n', fill[1])                    # 假設我們要取得標籤 1 的 onehot (10,28,28)
``````

``````""" 基本參數 """
epoch = 10
lr = 1e-5
batch = 4
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
z_dim = 100        # latent Space
c_dim = 1          # Image Channel
label_dim = 10     # label

transform = trans.Compose([
trans.ToTensor(),
trans.Normalize((0.5,),(0.5,)),
])

train_set = dset.MNIST(root='./mnist_data/',
train=True, transform=transform,

test_set = dset.MNIST(root='./mnist_data/',
train=False,
transform=transform,

dataset = train_set,
batch_size = batch,
shuffle=True,
drop_last=True
)

dataset = test_set,
batch_size = batch,
shuffle=False
)

""" 訓練相關 """

D = Discriminator(c_dim, label_dim).to(device)
G = Generator(z_dim, label_dim).to(device)
loss_fn = nn.BCELoss()
D_avg_loss = []
G_avg_loss = []

img = []
ls_time = []
``````

``````    """ 看到很多範例都有手動調整學習率 """
if epoch == 8:
G_opt.param_groups[0]['lr'] /= 5
D_opt.param_groups[0]['lr'] /= 5
``````

``````""" 訓練 D """

x_real = data.to(device)
y_real = torch.ones(batch, ).to(device)
c_real = fill[label].to(device)

y_real_predict = D(x_real, c_real).squeeze()        # (-1, 1, 1, 1) -> (-1, )
d_real_loss = loss_fn(y_real_predict, y_real)
d_real_loss.backward()

noise = torch.randn(batch, z_dim, 1, 1, device = device)
noise_label = (torch.rand(batch, 1) * label_dim).type(torch.LongTensor).squeeze()
noise_label_onehot = onehot[noise_label].to(device)   #隨機產生label (-1, )

x_fake = G(noise, noise_label_onehot)       # 生成假圖
y_fake = torch.zeros(batch, ).to(device)    # 給予標籤 0
c_fake = fill[noise_label].to(device)       # 轉換成對應的 10,28,28 的標籤

y_fake_predict = D(x_fake, c_fake).squeeze()
d_fake_loss = loss_fn(y_fake_predict, y_fake)
d_fake_loss.backward()
D_opt.step()

""" 訓練 G """

noise = torch.randn(batch, z_dim, 1, 1, device = device)
noise_label = (torch.rand(batch, 1) * label_dim).type(torch.LongTensor).squeeze()
noise_label_onehot = onehot[noise_label].to(device)   #隨機產生label (-1, )

x_fake = G(noise, noise_label_onehot)
#y_fake = torch.ones(batch, ).to(device)    #這邊的 y_fake 跟上述的 y_real 一樣，都是 1
c_fake = fill[noise_label].to(device)

y_fake_predict = D(x_fake, c_fake).squeeze()
g_loss = loss_fn(y_fake_predict, y_real)    #直接用 y_real 更直觀
g_loss.backward()
G_opt.step()

D_loss.append(d_fake_loss.item() + d_real_loss.item())
G_loss.append(g_loss.item())
``````

 1 2 3 4 5 6 7 8 9 10

### 下载

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw