# 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