I am working on the file "Homework/Principles of Artificial Neural Networks/Week 10 GAN 2/DL_WEEK10.ipynb".
The first task is:
答：
可以观察到生成网络的loss曲线变化的程度要较前两次训练的小，说明得到的G的能力相比前两次训练得到的G要强。
不过，在最终输出的图片中，肉眼分辨是效果比前两次训练差，这大概是输出选择的那一代中G的效果比较差。

若输入的类别标签不用one-hot的向量表示,我们一开始先为每个类随机生成一个随机向量,然后使用这个向量作为类别标签,这样对结果会有改变吗?试尝试运行下面代码,与之前的结果对比,说说有什么不同?
Can you write Python code for it?

vecs = torch.randn(class_num, class_num)
fills = vecs.unsqueeze(2).unsqueeze(3).expand(class_num, class_num, image_size, image_size)
print(vecs)
print(fills)

# hyper params

# device : cpu or cuda:0/1/2/3
device = torch.device('cuda:2')

# G and D model
G = DCGenerator(image_size=image_size, latent_dim=latent_dim, output_channel=image_channel, class_num=class_num)
D = DCDiscriminator(image_size=image_size, input_channel=image_channel, class_num=class_num)
G.to(device)
D.to(device)

# G and D optimizer, use Adam or SGD
G_optimizer = optim.Adam(G.parameters(), lr=learning_rate, betas=betas)
D_optimizer = optim.Adam(D.parameters(), lr=learning_rate, betas=betas)

d_loss_hist, g_loss_hist = run_gan(trainloader, G, D, G_optimizer, D_optimizer, bceloss,
                                   n_epochs, device, latent_dim, class_num)
loss_plot(d_loss_hist, g_loss_hist)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 10 GAN 2/DL_WEEK10.ipynb".
The first task is:
答：
可以观察到网络的结果是比较差的，因为类别标签是随机生成的，这导致生成器生成的假图是很容易被判别器正确识别。loss曲线中，G的loss值上升的速率较快，基本不会在固定的范围内波动，而D的loss值也有下降的趋势，可以看出该生成网络的效果是不如前面三次训练所得到的网络。
这大概是因为生成器的生成结果与类别标签的关系随机性强，判别器因此更加容易判断该图为假图，而生成网络得到的调整弱，因此能力较前面三次训练弱。
Image-image translation
下面介绍一个使用CGAN来做Image-to-Image Translation的模型--pix2pix。
Can you write Python code for it?

import os
import numpy as np
import math
import itertools
import time
import datetime
import sys

import torchvision
import torchvision.transforms as transforms

from torch.utils.data import DataLoader
from torchvision import datasets

import torch.nn as nn
import torch.nn.functional as F
import torch


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 10 GAN 2/DL_WEEK10.ipynb".
The first task is:
答：
观察两次训练的loss曲线，可以发现给图片和标签加上卷积之后，G的loss值一直稳定在一定的范围内，而没加上卷积处理的网络中，G的loss值一开始很低，后来逐渐升高。从loss曲线上分析，在第一次训练中G的变化更大。因此，第二次训练能得到效果更好的生成器。
从输出的图片上比较，也可以很明显可以看到第二次训练输出的结果比第一次好。

在D中,可以将输入图片通过1个卷积层然后和(尺寸与输入图片一致的)labels在维度1合并(通道上合并),再一起送去接下来的网络结构.网络部分结构已经在DCDiscriminator中写好,请在补充forward函数完成上述功能,并再次使用同样的数据集训练CGAN.与之前的结果对比,说说有什么不同?
Can you write Python code for it?

class DCDiscriminator2(nn.Module):
    def __init__(self, image_size=32, input_channel=1, class_num=3, sigmoid=True):
        super().__init__()
        self.image_size = image_size
        self.input_channel = input_channel
        self.class_num = class_num

        self.fc_size = image_size // 8

        #       model : img -> conv1
        #               labels -> maxpool
        #       (img U labels) -> Conv2d(3,2,1) -> BN -> LeakyReLU
        #       Conv2d(3,2,1) -> BN -> LeakyReLU

        self.conv1 = nn.Sequential(nn.Conv2d(input_channel, 128, 3, 2, 1),
                                     nn.BatchNorm2d(128))
        self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2)

        self.conv = nn.Sequential(
            nn.LeakyReLU(0.2),
            nn.Conv2d(128 + class_num, 256, 3, 2, 1),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2),
            nn.Conv2d(256, 512, 3, 2, 1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2),
        )

        # fc: Linear -> Sigmoid
        self.fc = nn.Sequential(
            nn.Linear(512 * self.fc_size * self.fc_size, 1),
        )
        if sigmoid:
            self.fc.add_module('sigmoid', nn.Sigmoid())
        initialize_weights(self)

    def forward(self, img, labels):
        """
        img : input image
        labels : (batch_size, class_num, image_size, image_size)
                the i-th channel is filled with 1, and others is filled with 0.
        """
        """
        To Do
        """
        input_img = self.conv1(img)
        input_labels = self.maxpool(labels)
        input_ = torch.cat((input_img, input_labels), dim=1)
        out = self.conv(input_)
        out = out.view(out.shape[0], -1)
        out = self.fc(out)

        return out

# hyper params
# device : cpu or cuda:0/1/2/3
device = torch.device('cuda:2')

# G and D model
G = DCGenerator(image_size=image_size, latent_dim=latent_dim, output_channel=image_channel, class_num=class_num)
D =...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 10 GAN 2/DL_WEEK10.ipynb".
The first task is:
训练部分的代码代码与之前相似, 不同的地方在于要根据类别生成y_vec(one-hot向量如类别2对应[0,1,0,0,0,0,0,0,0,0])和y_fill(将y_vec扩展到大小为(class_num, image_size, image_size),正确的类别的channel全为1,其他channel全为0),分别输入G和D作为条件变量.其他训练过程与普通的GAN相似.我们可以先为每个类别标签生成vecs和fills.
Can you write Python code for it?

# class number
class_num = 10

# image size and channel
image_size=32
image_channel=1

# vecs: one-hot vectors of size(class_num, class_num)
# fills: vecs expand to size(class_num, class_num, image_size, image_size)
vecs = torch.eye(class_num)
fills = vecs.unsqueeze(2).unsqueeze(3).expand(class_num, class_num, image_size, image_size)

print(vecs)
print(fills)

def train(trainloader, G, D, G_optimizer, D_optimizer, loss_func, device, z_dim, class_num):
    """
    train a GAN with model G and D in one epoch
    Args:
        trainloader: data loader to train
        G: model Generator
        D: model Discriminator
        G_optimizer: optimizer of G(etc. Adam, SGD)
        D_optimizer: optimizer of D(etc. Adam, SGD)
        loss_func: Binary Cross Entropy(BCE) or MSE loss function
        device: cpu or cuda device
        z_dim: the dimension of random noise z

    """
    # set train mode
    D.train()
    G.train()

    D_total_loss = 0
    G_total_loss = 0

    for i, (x, y) in enumerate(trainloader):
        x = x.to(device)
        batch_size_ = x.size(0)
        image_size = x.size(2)

        # real label and fake label
        real_label = torch.ones(batch_size_, 1).to(device)
        fake_label = torch.zeros(batch_size_, 1).to(device)

        # y_vec: (batch_size, class_num) one-hot vector, for example, [0,0,0,0,1,0,0,0,0,0] (label: 4)
        y_vec = vecs[y.long()].to(device)
        # y_fill: (batch_size, class_num, image_size, image_size)
        # y_fill: the i-th channel is filled with 1, and others is filled with 0.
        y_fill = fills[y.long()].to(device)

        z = torch.rand(batch_size_, z_dim).to(device)

        # update D network
        # D optimizer zero grads
        D_optimizer.zero_grad()

        # D real loss from real images
        d_real = D(x, y_fill)
        d_real_loss = loss_func(d_real, real_label)

        # D fake loss from fake images generated by G
        g_z = G(z, y_vec)
        d_fake = D(g_z,...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
1. Classifying Names with a Character-Level RNN

We will be building and training a basic character-level RNN to classify
words. A character-level RNN reads words as a series of characters -
outputting a prediction and "hidden state" at each step, feeding its
previous hidden state into each next step. We take the final prediction
to be the output, i.e. which class the word belongs to.
Specifically, we'll train on a few thousand surnames from 18 languages
of origin, and predict which language a name is from based on the
spelling:
1.1 Preparing the Data
Download the data from here and extract it to the current directory.
Included in the data/names directory are 18 text files named as
"[Language].txt". Each file contains a bunch of names, one name per
line, mostly romanized (but we still need to convert from Unicode to
ASCII).
We'll end up with a dictionary of lists of names per language,
{language: [names ...]}. The generic variables "category" and "line"
(for language and name in our case) are used for later extensibility.
Can you write Python code for it?

from __future__ import unicode_literals, print_function, division
from io import open
import glob
import os

def findFiles(path): return glob.glob(path)

print(findFiles('data/names/*.txt'))

import unicodedata
import string

all_letters = string.ascii_letters + " .,;'"
n_letters = len(all_letters)

# Turn a Unicode string to plain ASCII, thanks to https://stackoverflow.com/a/518232/2809427
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
        and c in all_letters
    )

print(unicodeToAscii('Ślusàrski'))

# Build the category_lines dictionary, a list of names per language
all_categories = []
category_lines = {}

# Split it into training set and validation set
training_lines = {}
validation_lines = {}

# Read a file and split into lines
def readLines(filename):
    lines = open(filename, encoding='utf-8').read().strip().split('\n')
    return [unicodeToAscii(line) for line in lines]

for filename in findFiles('data/names/*.txt'):
    category = os.path.splitext(os.path.basename(filename))[0]
    all_categories.append(category)
    lines = readLines(filename)
    category_lines[category] = lines

    num_of_training_set = int(len(lines)*0.8)
    training_lines[category]   = lines[:num_of_training_set]
    validation_lines[category] = lines[num_of_training_set:]

n_categories = len(all_categories)
print(n_categories)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
1.3 Creating the Network
Before autograd, creating a recurrent neural network in Torch involved
cloning the parameters of a layer over several timesteps. The layers
held hidden state and gradients which are now entirely handled by the
graph itself. This means you can implement a RNN in a very "pure" way,
as regular feed-forward layers.
Inside the forward function, we need to Loop
<img src="https://colah.github.io/posts/2015-08-Understanding-LSTMs/img/RNN-unrolled.png" width = "50%" />
Can you write Python code for it?

import torch.nn as nn

class BaseRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(BaseRNN, self).__init__()

        self.hidden_size = hidden_size

        # input to hidden
        self.i2h = nn.Linear(input_size,  hidden_size)
        # hidden to hidden
        self.h2h = nn.Linear(hidden_size, hidden_size)
        #############################################
        #
        #  Change your activation function here
        #
        #############################################
        # self.activation = nn.Tanh()
        self.activation = nn.ReLU()
        # hidden to output
        self.h2o = nn.Linear(hidden_size, output_size)

    def step(self, letter, hidden):
        i2h = self.i2h(letter)
        h2h = self.h2h(hidden)
        hidden = self.activation( h2h+i2h )

        output = self.h2o(hidden)
        return output, hidden

    def forward(self, word):
        hidden = self.initHidden(is_cuda)
        for i in range(word.size()[0]):
            # Only the last output will be used to predict
            output, hidden = self.step(word[i], hidden)
        return output

    def initHidden(self, is_cuda=True):
        if is_cuda:
            return torch.zeros(1, self.hidden_size).cuda()
        else:
            return torch.zeros(1, self.hidden_size)

n_hidden = 128
rnn = BaseRNN(n_letters, n_hidden, n_categories)
if is_cuda:
    rnn = rnn.cuda()

###############################################################################
#
# Finish a the following model, and train it
#
###############################################################################

class DeeperRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(DeeperRNN, self).__init__()
        self.hidden1_size = hidden_size
        self.hidden2_size = hidden_size
        self.layer1 = BaseRNN(input_size, hidden_size, hidden_size)
        self.layer2 = BaseRNN(hidden_size, hidden_size, output_size)

    def step(self,...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
1.4.3 Plotting the Results

Plotting the historical loss from all_losses shows the network
learning:
Can you write Python code for it?


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
1.5 Evaluating the Results
To see how well the network performs on different categories, we will
create a confusion matrix, indicating for every actual language (rows)
which language the network guesses (columns). To calculate the confusion
matrix a bunch of samples are run through the network with
evaluate(), which is the same as train() minus the backprop.
Can you write Python code for it?

# Keep track of correct guesses in a confusion matrix
confusion_training   = torch.zeros(n_categories, n_categories)
confusion_validation = torch.zeros(n_categories, n_categories)
n_confusion = 5000

# Just return an output given a line
def evaluate(line_tensor):
    rnn.eval()
    output = rnn(line_tensor)
    return output

# Go through a bunch of examples and record which are correctly guessed
for i in range(n_confusion):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    output = evaluate(line_tensor)
    guess, guess_i = categoryFromOutput(output)
    category_i = all_categories.index(category)
    confusion_training[category_i][guess_i] += 1

# Go through a bunch of examples and record which are correctly guessed
for i in range(n_confusion):
    category, line, category_tensor, line_tensor = randomValidationExample()
    output = evaluate(line_tensor)
    guess, guess_i = categoryFromOutput(output)
    category_i = all_categories.index(category)
    confusion_validation[category_i][guess_i] += 1

# catcul acc
right_train = 0
right_valid = 0
for i in range(n_categories):
    right_train += confusion_training[i][i]
    right_valid += confusion_validation[i][i]
acc_train = right_train / n_confusion
acc_valid = right_valid / n_confusion

# Normalize by dividing every row by its sum and
for i in range(n_categories):
    confusion_training[i] = confusion_training[i] / confusion_training[i].sum()
    confusion_validation[i] = confusion_validation[i] / confusion_validation[i].sum()

# Set up plot
fig = plt.figure()
ax1 = fig.add_subplot(121)
cax1 = ax1.matshow(confusion_training.numpy())

ax2 = fig.add_subplot(122)
cax2 = ax2.matshow(confusion_validation.numpy())

# Set up axes
ax1.set_xticklabels([''] + all_categories, rotation=90)
ax1.set_yticklabels([''] + all_categories)
ax2.set_xticklabels([''] + all_categories, rotation=90)

# sphinx_gallery_thumbnail_number = 2
plt.show()

print("Traing set Acc is", acc_train.item())
print("validation set Acc is", acc_valid.item())


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
1.7 Exercises
==============================================================================================================
1、在第1.4.3节中编写代码，打印出训练过程当中，loss值的变化，（loss已经在训练过程中记录，找出并画成曲线即可，你也可以选择自己修改训练过程中的代码，自己实现重新画成更加漂亮的曲线。）
2、在第1.6节，将自己的姓名的每一个字的拼音输入进去，进行预测，看模型预测是否正确（保留上面的预测结果）。如名字为张三，则输入predict('Zhang')和predict('San').
3、编辑该cell，在下面一行回答问题。当前的BaseRNN中使用的激活函数修改成relu（在第1.3小节中），然后重新训练模型，看性能是否有变化。
当前的激活函数是（Tanh），训练集准确率为：（0.7675999999046326）验证集的准确率为：（0.39480000734329224）
激活函数为Relu时的训练准确率为：（0.770799994468689）验证集的准确率为：（0.4124000072479248）
4、编辑该cell，在下面一行记录当前的模型的准确率，然后，回到1.3中，编辑DeeperRNN类，使其比BaseRNN类更深一层，并且重新训练这个更深的网络，看新能是否有提高（记得去掉DeeperRNN那一个cell中的最后两行的注释）。对于RNN的隐藏层的增加可阅读下一个cell的补充理解
BaseRNN的训练集准确率为：（0.770799994468689）验证集的准确率为：（0.4124000072479248）
DeeperRNN的训练准确率为：（0.8001999855041504）验证集的准确率为：（0.4189999997615814）
补充材料：

RNNs are neural networks and everything works monotonically better (if done right) if you put on your deep learning hat and start stacking models up like pancakes. For instance, we can form a 2-layer recurrent network as follows:
python
y1 = rnn1.step(x)
y = rnn2.step(y1)
In other words we have two separate RNNs: One RNN is receiving the input vectors and the second RNN is receiving the output of the first RNN as its input. Except neither of these RNNs know or care - it’s all just vectors coming in and going out, and some gradients flowing through each module during backpropagation.

原文阅读点击这里
多层hidden网络结构图如下图所示，其中虚线输出的output表示改output值被丢弃，没有进一步使用：
<img src="./images/multihidden.png" width = "40%" />
2. Generating Names with a Character-Level RNN

We are still hand-crafting a small RNN with a few linear layers. The big
difference is instead of predicting a category after reading in all the
letters of a name, we input a category and output one letter at a time.
Recurrently predicting characters to form language (this could also be
done with words or other higher order constructs) is often referred to
as a "language model".
2.1 Creating the Network
This network extends the last tutorial's...
Can you write Python code for it?

import torch
import torch.nn as nn

class GenerateRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(GenerateRNN, self).__init__()
        self.hidden_size = hidden_size
        self.c2h = nn.Linear(n_categories, hidden_size)
        self.i2h = nn.Linear(input_size,   hidden_size)
        self.h2h = nn.Linear(hidden_size,  hidden_size)
        self.activation = nn.Tanh()
        self.h2o = nn.Linear(hidden_size,  output_size)
        self.dropout = nn.Dropout(0.2)

        self.softmax = nn.LogSoftmax(dim=1)

    def forward(self, category, input, hidden):
        c2h = self.c2h(category)
        i2h = self.i2h(input)
        h2h = self.h2h(hidden)

        hidden = self.activation( c2h+i2h+h2h )

        dropout = self.dropout(self.h2o(hidden))
        output = self.softmax(dropout)
        return output, hidden

    def initHidden(self, is_cuda=True):
        if is_cuda:
            return torch.zeros(1, self.hidden_size).cuda()
        else:
            return torch.zeros(1, self.hidden_size)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2.2 Training
2.2.1 Preparing for Training
Can you write Python code for it?

import random

# Random item from a list
def randomChoice(l):
    return l[random.randint(0, len(l) - 1)]

# Get a random category and random line from that category
def randomTrainingPair():
    category = randomChoice(all_categories)
    line = randomChoice(category_lines[category])
    return category, line


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2.2.2 Training the Network
In contrast to classification, where only the last output is used, we
are making a prediction at every step, so we are calculating loss at
every step.
The magic of autograd allows you to simply sum these losses at each step
and call backward at the end.
For the loss function nn.NLLLoss is appropriate, since the last
layer of the RNN is nn.LogSoftmax.
For the different between nn.NLLLoss and the nn.LogSoftmax, we could Look
at the source code, the most import line is quote as follow:
``` python
 def cross_entropy(input, target):
    return nll_loss(log_softmax(input, 1))
```
In a word, Cross entropy combines log_softmax and nll_loss in a single function.
Can you write Python code for it?

criterion = nn.NLLLoss()

learning_rate = 0.0005

def train(category_tensor, input_line_tensor, target_line_tensor):
    target_line_tensor.unsqueeze_(-1)
    hidden = rnn.initHidden(is_cuda)

    rnn.zero_grad()

    loss = 0

    # Take care of the loss function,
    # it could be visualized as the following figure
    for i in range(input_line_tensor.size(0)):
        output, hidden = rnn(category_tensor, input_line_tensor[i], hidden)
        l = criterion(output, target_line_tensor[i])
        loss += l

    loss.backward()

    for p in rnn.parameters():
        if hasattr(p.grad, "data"):
            p.data.add_(-learning_rate, p.grad.data)

    return output, loss.item() / input_line_tensor.size(0)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2.2.3 Plotting the Losses
Plotting the historical loss from all_losses shows the network
learning:
Can you write Python code for it?

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

plt.figure()
plt.plot(all_losses)
plt.show()


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2.3 Sampling the Network
To sample we give the network a letter and ask what the next one is,
feed that in as the next letter, and repeat until the EOS token.

Create tensors for input category, starting letter, and empty hidden
   state
Create a string output_name with the starting letter

Up to a maximum output length,

Feed the current letter to the network

Get the next letter from highest output, and next hidden state
If the letter is EOS, stop here

If a regular letter, add to output_name and continue

Return the final name

Note:
   Rather than having to give it a starting letter, another
   strategy would have been to include a "start of string" token in
   training and have the network choose its own starting letter.
Can you write Python code for it?

max_length = 20

# Sample from a category and starting letter
def sample(category, start_letter='A'):
    with torch.no_grad():
        # no need to track history in sampling
        category_tensor = categoryTensor(category)
        input = inputTensor(start_letter)
        hidden = rnn.initHidden(is_cuda)

        output_name = start_letter

        for i in range(max_length):
            output, hidden = rnn(category_tensor, input[0], hidden)
            topv, topi = output.topk(1)
            topi = topi[0][0]
            if topi == n_letters - 1:
                break
            else:
                letter = all_letters[topi]
                output_name += letter
            input = inputTensor(letter)

        return output_name

# Get multiple samples from one category and multiple starting letters
def samples(category, start_letters='ABC'):
    for start_letter in start_letters:
        print(sample(category, start_letter))
    print("\n")

samples('Russian', 'CCZZYY')

samples('German',  'CCZZYY')

samples('Spanish', 'CCZZYY')

samples('Chinese', 'CCZZYY')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2.4 Exercises
=======================================================================================================
1、反复输入自己名字的首字母，观察网络的生成的名字是否一样（保留以你自己的名字的首字母为程序输入的结果）
答：网络生成的名字并不一样。
Can you write Python code for it?

samples('Chinese', 'CXJ')
samples('Chinese', 'CXJ')
samples('Chinese', 'CXJ')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
2、请回答：为什么这个模型这个模型训练好了之后，输入同样的参数会产生不一样的结果？并在上面的程序中验证你自己的猜测
答：因为RNN中使用了Dropout来防止过拟合，因此每次forward的参数都可能不一样。
3、这是一个生成模型，该模型只输入了一个字母，就可以预测一个单词，请问改模型在开始预测什么时候终止预测新的字符，即这个生成模型是如何确定生成的单词的长度的？
答：在预估新的字符为'时终止预测，即新的预测字符的下标为n_letters - 1时。如果预测的单词长度超过max_length，也终止预测。
4、根据下一个cell的提示，以impantance sampling的方式，实现程序在同样输入的情况下，可能生成不一样的名字。
Importance sampling指的是，在生成单词每一个字母的时候，一般的生成模型都有是选择输出结果中概率值最大的字母作为预测，而Importance sampling的方式则是按照各个字母的输出概率来选择输出哪个模型。举个栗子，当程序输入一个字母C的时候，程序预测下一个字母为[a, o, e]的概率分别为[0.7, 0.2, 0.1], 普通的做法是直接选择概率值最大的a作为预测结果，而Importance Sampling的方式则是以70%的概率选择a，以20%的概率选择o，以10%的该路选择e，最终选到哪个有一定的随机性。
请根据你的理解，补充完成下面的Importance sampling的预测。
Tips: 可使用numpy.random.choice函数，文档请点击这里
Tips: 这部分的代码与上面的代码的不同之处仅仅在于如何选取预测值，所以，你只需要在下面指定的区域编辑代码即可。
Tips: 直接使用模型输出的概率值会比较麻烦，因为模型的概率输出值会很小，因为这里有50多种字符，所以，一个更加明智的做法是只在概率值最高的前3个或者5个中选择，记得要将概率使用softmax等函数将其转换到和为1.
Tips: 更加简单的做法是，仅仅使用模型输出的概率值进行排序，然后使用指定的概率值进行选择，如，指定选择概率最大的那个字母的概率值为0.5，第二大的为0.3，第三大的为0.2，然后可以直接输入[0.5, 0.3, 0.2].
Can you write Python code for it?

import numpy as np

max_length = 20

# Sample from a category and starting letter
def sample(category, start_letter='A'):
    with torch.no_grad():  # no need to track history in sampling
        # initial the model and input
        category_tensor = categoryTensor(category)
        input = inputTensor(start_letter)
        hidden = rnn.initHidden(is_cuda)

        output_name = start_letter

        for i in range(max_length):
            # get the output from model
            output, hidden = rnn(category_tensor, input[0], hidden)
            # change it into probability
            output = torch.exp(output)

            #################################################################################
            # code here
            topv, topi = output.topk(5)
            topv = topv / topv.sum()
            index = np.random.choice(5, 1, p=topv.cpu().numpy()[0])
            ################################################################################

            topi = topi[0][index]
            if topi == n_letters - 1:
                break
            else:
                letter = all_letters[topi]
                output_name += letter
            input = inputTensor(letter)

        return output_name

# Get multiple samples from one category and multiple starting letters
def samples(category, start_letters='ABC'):
    for start_letter in start_letters:
        print(sample(category, start_letter))
    print("\n")

rnn.eval()

samples('Russian', 'CYY')

samples('German', 'CCY')

samples('Spanish', 'CCZZYY')

samples('Chinese', 'CCCZZZYYY')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
As you can see the output is a <1 x n_categories> Tensor, where
every item is the likelihood of that category (higher is more likely).
1.4 Training
1.4.1 Preparing for Training
Before going into training we should make a few helper functions. The
first is to interpret the output of the network, which we know to be a
likelihood of each category. We can use Tensor.topk to get the index
of the greatest value:
Can you write Python code for it?

def categoryFromOutput(output):
    top_n, top_i = output.topk(1)
    category_i = top_i[0].item()
    return all_categories[category_i], category_i

print(categoryFromOutput(output))


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
For convenience during training we'll make a randomTrainingExample
function that fetches a random (category, line) pair and turns them into
the required (category, input, target) tensors.
Can you write Python code for it?

# Make category, input, and target tensors from a random category, line pair
def randomTrainingExample():
    category, line = randomTrainingPair()
    category_tensor = categoryTensor(category)
    input_line_tensor = inputTensor(line)
    target_line_tensor = targetTensor(line)
    return category_tensor, input_line_tensor, target_line_tensor


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
For each timestep (that is, for each letter in a training word) the
inputs of the network will be
(category, current letter, hidden state) and the outputs will be
(next letter, next hidden state). So for each training set, we'll
need the category, a set of input letters, and a set of output/target
letters.
Since we are predicting the next letter from the current letter for each
timestep, the letter pairs are groups of consecutive letters from the
line - e.g. for "ABCD<EOS>" we would create ("A", "B"), ("B", "C"),
("C", "D"), ("D", "EOS").

The category tensor is a one-hot
tensor of size
<1 x n_categories>. When training we feed it to the network at every
timestep - this is a design choice, it could have been included as part
of initial hidden state or some other strategy.
Or you could look at here for another description. I think this image could
tell you everything.
<img src="./images/charseq.jpeg" width = "50%" />
Can you write Python code for it?

# One-hot vector for category
def categoryTensor(category):
    li = all_categories.index(category)
    tensor = torch.zeros(1, n_categories)
    tensor[0][li] = 1
    if is_cuda:
        tensor = tensor.cuda()
    return tensor

# One-hot matrix of first to last letters (not including EOS) for input
def inputTensor(line):
    tensor = torch.zeros(len(line), 1, n_letters)
    for li in range(len(line)):
        letter = line[li]
        tensor[li][0][all_letters.find(letter)] = 1
    if is_cuda:
        tensor = tensor.cuda()
    return tensor

# LongTensor of second letter to end (EOS) for target
def targetTensor(line):
    letter_indexes = [all_letters.find(line[li]) for li in range(1, len(line))]
    letter_indexes.append(n_letters - 1) # EOS
    tensor = torch.LongTensor(letter_indexes)
    if is_cuda:
        tensor = tensor.cuda()
    return tensor


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
For the sake of efficiency we don't want to be creating a new Tensor for
every step, so we will use lineToTensor instead of
letterToTensor and use slices. This could be further optimized by
pre-computing batches of Tensors.
Each loop in side forward function will:

Create input and target tensors
Create a zeroed initial hidden state

Read each letter in and

Keep hidden state for next letter

Compare final output to target

Back-propagate
Return the output and loss
Can you write Python code for it?

input = lineToTensor('Albert')
print(n_hidden)

output = rnn(input)
print(output)
print(output.shape)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
Now we have category_lines, a dictionary mapping each category
(language) to a list of lines (names). We also kept track of
all_categories (just a list of languages) and n_categories for
later reference.
Can you write Python code for it?


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
Now we just have to run that with a bunch of examples. Since the
train function returns both the output and loss we can print its
guesses and also keep track of loss for plotting. Since there are 1000s
of examples we print only every print_every examples, and take an
average of the loss.
Can you write Python code for it?

import time
import math

n_iters = 100000
print_every = 5000
plot_every = 1000

# Keep track of losses for plotting
current_loss = 0
all_losses = []

def timeSince(since):
    now = time.time()
    s = now - since
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)

start = time.time()

for iter in range(1, n_iters + 1):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    output, loss = train(category_tensor, line_tensor)
    current_loss += loss

    # Print iter number, loss, name and guess
    if iter % print_every == 0:
        guess, guess_i = categoryFromOutput(output)
        correct = '✓' if guess == category else '✗ (%s)' % category
        print('%d %d%% (%s) %.4f %s / %s %s' % (iter, iter / n_iters * 100, timeSince(start), loss, line, guess, correct))

    # Add current loss avg to list of losses
    if iter % plot_every == 0:
        all_losses.append(current_loss / plot_every)
        current_loss = 0


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
The loss function could be shown as the following picture. The network forward through entire sequence to compute loss, then backward through entire sequence to compute gradient
<img src="./images/rnn-loss.png" width = "50%" />
To keep track of how long training takes I am adding a timeSince(timestamp) function which returns a human readable string:
Can you write Python code for it?

import time
import math

def timeSince(since):
    now = time.time()
    s = now - since
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
To run a step of this network we need to pass an input (in our case, the
Tensor for the current letter) and a previous hidden state (which we
initialize as zeros at first). We'll get back the output (probability of
each language) and a next hidden state (which we keep for the next
step).
Can you write Python code for it?

input = letterToTensor('A')
hidden =torch.zeros(1, n_hidden)
hidden = hidden.cuda() if is_cuda else hidden

output, next_hidden = rnn.step(input, hidden)
print(output.shape)
print(next_hidden.shape)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
Training is business as usual - call train a bunch of times and wait a few minutes, printing the current time and loss every print_every examples, and keeping store of an average loss per plot_every examples in all_losses for plotting later.
Can you write Python code for it?

is_cuda = False

rnn = GenerateRNN(n_letters, 128, n_letters)
rnn = rnn.cuda() if is_cuda else rnn

n_iters = 100000
print_every = 5000
plot_every = 500
all_losses = []
total_loss = 0 # Reset every plot_every iters

start = time.time()

for iter in range(1, n_iters + 1):
    output, loss = train(*randomTrainingExample())
    total_loss += loss

    if iter % print_every == 0:
        print('%s (%d %d%%) %.4f' % (timeSince(start), iter, iter / n_iters * 100, total_loss/iter))

    if iter % plot_every == 0:
        all_losses.append(total_loss / iter)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 11 RNN 1/week11-rnn-part1.ipynb".
The first task is:
We will also want a quick way to get a training example (a name and its
language):
Can you write Python code for it?

import random

def randomChoice(l):
    return l[random.randint(0, len(l) - 1)]

def randomTrainingExample():
    category = randomChoice(all_categories)
    # attention: split training set
    line = randomChoice(training_lines[category])
    category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
    category_tensor = category_tensor.cuda() if is_cuda else category_tensor
    line_tensor = lineToTensor(line)
    return category, line, category_tensor, line_tensor

def randomValidationExample():
    category = randomChoice(all_categories)
    # attention: split validation set
    line = randomChoice(validation_lines[category])
    category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
    category_tensor = category_tensor.cuda() if is_cuda else category_tensor
    line_tensor = lineToTensor(line)
    return category, line, category_tensor, line_tensor

for i in range(10):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    print('category =', category, '/ line =', line)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
2.4 Memory Module
Memory 模块的示意图如下所示：

在这里，我们先获取经过Input Module编码过后的信息$F$，然后进行输入Attention Mechanism中，迭代查找相关信息。Attention Mechanism中隐含了从Question Module中获得的对问题的文本描述进行GRU编码之后的信息输入。
Attention实际上是在比较和查找Question 和 Input之间的联系
Can you write Python code for it?

class AttentionGRUCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(AttentionGRUCell, self).__init__()
        self.hidden_size = hidden_size

        self.Wr = nn.Linear(input_size, hidden_size)
        self.Ur = nn.Linear(hidden_size, hidden_size)
        self.W = nn.Linear(input_size, hidden_size)
        self.U = nn.Linear(hidden_size, hidden_size)

    def forward(self, fact, C, g):
        r = torch.sigmoid(self.Wr(fact) + self.Ur(C))
        h_tilda = torch.tanh(self.W(fact) + r * self.U(C))
        g = g.unsqueeze(1).expand_as(h_tilda)
        h = g * h_tilda + (1 - g) * C
        return h

class AttentionGRU(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(AttentionGRU, self).__init__()
        self.hidden_size = hidden_size
        self.AGRUCell = AttentionGRUCell(input_size, hidden_size)

    def forward(self, facts, G):
        batch_num, sen_num, embedding_size = facts.size()
        C = Variable(torch.zeros(self.hidden_size)).cuda()
        for sid in range(sen_num):
            fact = facts[:, sid, :]
            g = G[:, sid]
            if sid == 0:
                C = C.unsqueeze(0).expand_as(fact)
            C = self.AGRUCell(fact, C, g)
        return C


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
2.5 Answer Module
Answer Module将Memory Module和Question Module的信息使用全连接层结合到一起，然后输出一个文本中所有单词的对应着问题答案的可能性。
Can you write Python code for it?

class AnswerModule(nn.Module):
    def __init__(self, vocab_size, hidden_size):
        super(AnswerModule, self).__init__()
        self.z = nn.Linear(2 * hidden_size, vocab_size)
        self.dropout = nn.Dropout(0.1)

    def forward(self, M, questions):
        M = self.dropout(M)
        concat = torch.cat([M, questions], dim=2).squeeze(1)
        z = self.z(concat)
        return z


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
3. Training and Test
3.1 Train the Network
bAbI数据集共有20个小的QA任务，为了训练简单，我们每次只训练一个任务，请将下面的一个 cell 中的 task_id 设置成你的学号的最后一个数字，尾号为 0 的同学将task_id设置为10
Can you write Python code for it?

task_id = 10

epochs = 3

if task_id in range(1, 21):
#def train(task_id):
    dset = BabiDataset(task_id)
    vocab_size = len(dset.QA.VOCAB)
    hidden_size = 80

    model = DMNPlus(hidden_size, vocab_size, num_hop=3, qa=dset.QA)
    model.cuda()

    best_acc = 0
    optim = torch.optim.Adam(model.parameters())

    for epoch in range(epochs):
        dset.set_mode('train')
        train_loader = DataLoader(
                dset, batch_size=32, shuffle=True, collate_fn=pad_collate
                )

        model.train()
        total_acc = 0
        cnt = 0
        for batch_idx, data in enumerate(train_loader):
            contexts, questions, answers = data
            batch_size = contexts.size()[0]
            contexts  = Variable(contexts.long().cuda())
            questions = Variable(questions.long().cuda())
            answers   = Variable(answers.cuda())

            loss, acc = model.get_loss(contexts, questions, answers)
            optim.zero_grad()
            loss.backward()
            optim.step()

            total_acc += acc * batch_size
            cnt += batch_size
            if batch_idx % 20 == 0:
                print(f'[Task {task_id}, Epoch {epoch}] [Training] loss : {loss.item(): {10}.{8}}, acc : {total_acc / cnt: {5}.{4}}, batch_idx : {batch_idx}')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
3.2 Test
测试集上，该任务的准确率是多少
Can you write Python code for it?

dset.set_mode('test')
test_loader = DataLoader(
    dset, batch_size=100, shuffle=False, collate_fn=pad_collate
    )
test_acc = 0
cnt = 0

model.eval()
for batch_idx, data in enumerate(test_loader):
    contexts, questions, answers = data
    batch_size = contexts.size()[0]
    contexts = Variable(contexts.long().cuda())
    questions = Variable(questions.long().cuda())
    answers = Variable(answers.cuda())

    _, acc = model.get_loss(contexts, questions, answers)
    test_acc += acc * batch_size
    cnt += batch_size
print(f'Task {task_id}, Epoch {epoch}] [Test] Accuracy : {test_acc / cnt: {5}.{4}}')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
4. Exercise
1、QA问题和关系型数据库(如对关系型数据库有所遗忘的话，请点击这里，可稍作回顾)的检索有什么区别？简要说说你的理解
答：关系型数据库的检索是通过建立在相应字段上的索引和条件表达式来实现的，而QA问题的检索式根据记忆状态信息来实现的。关系型数据库可以在字段上建立索引，然后通过索引得到数据的存储位置，从而获取数据。QA问题将问答的知识以memory state的形式存储起来，训练时可以更新memory state，而检索时只需要将问题转换为相应的特征值，然后通过DMN产生输出，再转换为自然语言。
2、bAbI数据集的qa问题是怎么划分为了20个任务，划分的依据是什么？请到根据上文的提示，打开数据集文件进行观察，然后回答
答：bAbI数据集的QA问题是以答案类型或答案依据来划分的。比如任务1是单个事实(句子)支持的答案，任务2、3是两个、三个事实(句子)支持的答案。比如任务10是回答yes、no、maybe，而任务7是回答数量。
3、编写几行代码，探索一下在这个数据集中一个Task中训练集有多少条，测试集有多少条(提示是下面一个cell的代码)
Can you write Python code for it?

# default dataset is training set
dataset = BabiDataset( task_id )
print('train set:', len(dataset))

# change the mode into "test", now dataset is testset
dataset.set_mode("test")
print('test set:', len(dataset))


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
<img src="./images/memory_equation.png" width = "50%" />
Can you write Python code for it?

class EpisodicMemory(nn.Module):
    def __init__(self, hidden_size):
        super(EpisodicMemory, self).__init__()
        self.AGRU = AttentionGRU(hidden_size, hidden_size)
        self.z1 = nn.Linear(4 * hidden_size, hidden_size)
        self.z2 = nn.Linear(hidden_size, 1)
        self.next_mem = nn.Linear(3 * hidden_size, hidden_size)

    def make_interaction(self, facts, questions, prevM):
        '''
        facts.size() -> (#batch, #sentence, #hidden = #embedding)
        questions.size() -> (#batch, 1, #hidden)
        prevM.size() -> (#batch, #sentence = 1, #hidden = #embedding)
        z.size() -> (#batch, #sentence, 4 x #embedding)
        G.size() -> (#batch, #sentence)
        '''
        batch_num, sen_num, embedding_size = facts.size()
        questions = questions.expand_as(facts)
        prevM = prevM.expand_as(facts)

        z = torch.cat([
            facts * questions,
            facts * prevM,
            torch.abs(facts - questions),
            torch.abs(facts - prevM)
        ], dim=2)

        z = z.view(-1, 4 * embedding_size)

        G = torch.tanh(self.z1(z))
        G = self.z2(G)
        G = G.view(batch_num, -1)
        G = F.softmax(G, dim=1)

        return G

    def forward(self, facts, questions, prevM):
        '''
        facts.size() -> (#batch, #sentence, #hidden = #embedding)
        questions.size() -> (#batch, #sentence = 1, #hidden)
        prevM.size() -> (#batch, #sentence = 1, #hidden = #embedding)
        G.size() -> (#batch, #sentence)
        C.size() -> (#batch, #hidden)
        concat.size() -> (#batch, 3 x #embedding)
        '''
        G = self.make_interaction(facts, questions, prevM)
        C = self.AGRU(facts, G)
        concat = torch.cat([prevM.squeeze(1), C, questions.squeeze(1)], dim=1)
        next_mem = F.relu(self.next_mem(concat))
        next_mem = next_mem.unsqueeze(1)
        return next_mem


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
从上面测试中，我们就清楚我们该如何使用这个Dataloader了。
同时，通过观察上面的输入和输出，我们应该看到：
1. <PAD>符号：在同一个batch中，所以的句子都进行了padding的操作，将一个batch中的所有的句子都补到和最长的句子一样长。同时，每个问题也是，比如batchsize为8，则说明这个batch中共有8个问题，但是有个故事可能句子多些，有的故事句子少些，统一padding到最多句子的形式就好了。在rnn中，我们本不必统一句子和文段的长度，在这里使用padding的方式固定长度是出于训练的需要，定长才能将句子和文段以batch的形式输入进行处理，是文本处理的一个常用的方式
2. <EOS>符号：end of sentence
3. 有的句子可以同时被多个问题引用到
4. 预测的答案是输入的sentences中所包含的单词中的一个。
2. Dynamic Memory Networks
在理解这个网络结构之前，一定要认真阅读上面提到的论文总结，上面的讲述很详细，很多很细节的地方，我在这里就不会跟那篇文章那样子对比得那么详细了，强烈建议先重新回顾一下上述论文的演变过程，对网络的运作有个清楚的认识，重温总结，点击这里
2.1 Architecture
网络的总体架构入下图所示。
<img src="./images/example.png" width = "50%" />
从上图中可以看出要实现这个QA系统，我们需要实现4个模块：Input Module, Memory Module, Question Module and Answer Module. 我们在下来的实现中看细节。
2.2 Input Module
首先导入一些常用的包
Can you write Python code for it?

import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
from torch.autograd import Variable


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
好了，根据这个查找表，我们就可以很顺利得将我们的数据还原出来了。只需要按照索引重新映射回来即可。
Can you write Python code for it?

def interpret_indexed_tensor(contexts, questions, answers):
    for n, data in enumerate(zip(contexts, questions, answers)):
        context  = data[0]
        question = data[1]
        answer   = data[2]

        print(n)
        for i, sentence in enumerate(context):
            s = ' '.join([references[elem.item()] for elem in sentence])
            print(f'{i}th sentence: {s}')
        s = ' '.join([references[elem.item()] for elem in question])
        print(f'question: {s}')
        s = references[answer.item()]
        print(f'answer:   {s}')

interpret_indexed_tensor(contexts, questions, answers)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 13 DMN/DL_week13_memory.ipynb".
The first task is:
好了，现在我们导入了dataloader之后，可以尝试将每一个batch的数据打印出来看看是什么样子的
Can you write Python code for it?

contexts, questions, answers = next(iter(dataloader))
print(contexts.shape)
print(questions.shape)
print(answers.shape)
print(contexts)
print(questions)
print(answers)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
1.2 Random seed setting for reproducibility
In order to make computations deterministic on your specific problem on one specific platform and PyTorch release, there are a couple of steps to take.
Can you write Python code for it?

'''step 3'''
# However, in the same exp, seed for torch and numpy doesn't be the same.
SEED = 47

# Sets the seed for generating random numbers, including GPU and CPU
torch.manual_seed(SEED)

# Deterministic algorithm for convolutional ops
torch.backends.cudnn.deterministic = True
# Deterministic alogorithm for cudnn, otherwise, cuddn would choose the fastest algorithm for every
# iteration ops, which cause variability and time consuming if input changes frequently.
torch.backends.cudnn.benchmark = False

# Seed the generator for Numpy
np.random.seed(SEED)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
2. Data split and Cross Validatioin
We would split Cifar10 into 5-fold and do cross validation.
The Cifar10 database (Modified National Institute of Standards and Technology database) s a collection of images that are commonly used to train machine learning and computer vision algorithms.
The CIFAR-10 dataset contains 60,000 32x32 color images in 10 different classes. The 10 different classes represent airplanes, cars, birds, cats, deer, dogs, frogs, horses, ships, and trucks. There are 6,000 images of each class
And they look like images below.

2.1 Using sklearn's method to do Five-fold split
TODO
Can you write Python code for it?

# Example to use kFold
from sklearn.model_selection import KFold
import numpy as np

train_transform = transforms.Compose([

])

dataset = torchvision.datasets.CIFAR10(root='./data',
                                      train=True, transform=train_transform,download=True)

data = dataset.train_data

# dataset.train_labels gets list object, we should transform to numpy for convinience
label = np.array(dataset.train_labels)

# set numpy random seed, we can get a determinate k-fold dataset
# np.random.seed(1)

kf = KFold(n_splits=5,shuffle=True)

for train_index, test_index in kf.split(data):
    print('train_index', train_index, 'test_index', test_index)
    train_data, train_label = data[train_index], label[train_index]
    test_data, test_label = data[test_index], label[test_index]

# here we use the last fold to be our trainset
dataset.train_data = train_data
dataset.train_labels = list(train_label)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
2.3 Hyper parameter
Can you write Python code for it?

'''step 4'''
# set hyper parameter
batch_size = 32
n_epochs = 50
learning_rate = 1e-3


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
2.4 Data augmentation
Using suitable data augmentation usually can get a better model. However, not all augmentation function are effective to all dataset It is advisable to choose favorable function for our dataset.
transform document:[https://pytorch.org/docs/stable/torchvision/transforms.html]
here are the most commonly used functions you may interest

torchvision.transforms.Compose()
torchvision.transforms.CenterCrop()
torchvision.transforms.Pad()
torchvision.transforms.RandomCrop()
torchvision.transforms.RandomHorizontalFlip()
torchvision.transforms.Resize()
torchvision.transforms.Normalize()
torchvision.transforms.ToPILImage()
torchvision.transforms.ToTensor()
torchvision.transforms.RandomRotation()

There is not standard answer for how to choose a suitable augmentation for your dataset, but we try to teach you what may be useful.
We use cifar10 as our dataset in this class. so, here are some suggestion when you use it.
The size of images in cifar10 are 32×32, it maybe not suitable to choose rotation operation for the images, becuase rotation will bring black pixels, those black pixels may exist after randomcrop operation.
we suggest that you can consider to use horizontal flip in nature dataset but not use vertical flip. becuase you know it is very rare for an object to be inverted.
Can you write Python code for it?

%matplotlib inline
from PIL import Image

# rotate 30°
transform_rotate = transforms.RandomRotation((30,30))

transform_horizontalflip = transforms.RandomHorizontalFlip(p=1)

transform_verticalflip = transforms.RandomVerticalFlip(p=1)

# the first image in cifar10 trainset
img = Image.open('./img/example.jpg')
plt.imshow(img)
plt.axis('off')
plt.show()

# a cat image
img1 = Image.open('./img/cat.jpeg')
plt.imshow(img1)
plt.axis('off')
plt.show()

# rotate the image
img2 = transform_rotate(img1)
plt.imshow(img2)
plt.axis('off')
plt.show()

# horizontal flip the image
img3 = transform_horizontalflip(img1)
plt.imshow(img3)
plt.axis('off')
plt.show()

# vertical flip the image
img4 = transform_verticalflip(img1)
plt.imshow(img4)
plt.axis('off')
plt.show()

'''step 5'''
'''
    the mean and variance below are from get_mean_std() function, every time you run the above function may get
    defferent value, because we use sampling
'''

'''
    notice: we usually will not use the dataset above, because its transform function has been change the numpy
    data to tensor data, but those transformations such as filp, rotation, crop, pad should be done before the
    data transforms to tensor.
'''

# transform1
train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),
#     transforms.RandomRotation(10),
    transforms.ToTensor(), # Convert a PIL Image or numpy.ndarray to tensor.
    # Normalize a tensor image with mean 0.1307 and standard deviation 0.3081
    transforms.Normalize((0.4931373, 0.48048604, 0.44251624), (0.24307655, 0.23882599, 0.25874203))
])

# transform2
# train_transform = transforms.Compose([
#         transforms.RandomCrop(32, padding=4),
#         transforms.RandomHorizontalFlip(),
#         transforms.ToTensor(),
#         transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))])

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.48723358, 0.48176482, 0.45129994), (0.24020454, 0.23832673, 0.25824794))
])

train_dataset =...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
3. classificatioon network
3.1 Original Original Resnet18
Can you write Python code for it?

resnet18 = torchvision.models.resnet18()
print(resnet18)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
3.2 Modified Resnet18 for out train dataset
As we can see, the resnet model we import from torchvison.models downsamples five times, It means that the size of feature maps in avgpooling layer is only 1×1, we may lose too much information during the convolution layers. It's worth noting that before enter the blocks, the input images have been downsapled tow times, we lose lots of information. so, fixing the first two downsaple layer may be a good choise.
Can you write Python code for it?

'''step 6'''
class ResNet18(nn.Module):
    def __init__(self):
        super(ResNet18,self).__init__()
        original_model = torchvision.models.resnet18()
        original_model.conv1.stride = 1
        self.feature_extractor = nn.Sequential(
            *(list(original_model.children())[0:3]),
            *(list(original_model.children())[4:-2]),
            nn.AdaptiveAvgPool2d(1)
        )
        self.fc = nn.Linear(512,10)

    def forward(self, x):
        out1 = self.feature_extractor(x)
        out1 = out1.view(out1.size(0),-1)
        out2 = self.fc(out1)
        return out2


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
4. Training
We would define training function here. Additionally, hyper-parameters, loss function, metric would be included here too.
4.1 Pre-set hyper-parameters
setting hyperparameters like below
hyper paprameters include following part

learning rate: usually we start from a quite bigger lr like 1e-1, 1e-2, 1e-3, and slow lr as epoch moves.
n_epochs: training epoch must set large so model has enough time to converge. Usually, we will set a quite big epoch at the first training time.
batch_size: usually, bigger batch size mean's better usage of GPU and model would need less epoches to converge. And the exponent of 2 is used, eg. 2, 4, 8, 16, 32, 64, 128. 256.
Can you write Python code for it?

'''step 7'''
# create a model object
# model = torchvision.models.resnet18()
# model.avgpool = nn.AdaptiveAvgPool2d(1)
# model.fc = nn.Linear(512,10)
model = ResNet18()
model.to(device)
# Cross entropy
loss_fn = torch.nn.CrossEntropyLoss()
# l2_norm can be done in SGD
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=0.9)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
4.2 Initialize model parameters
Pytorch provide default initialization (uniform intialization) for linear layer. But there is still some useful intialization method.
Read more about initialization from this link
torch.nn.init.normal_
    torch.nn.init.uniform_
    torch.nn.init.constant_
    torch.nn.init.eye_
    torch.nn.init.xavier_uniform_
    torch.nn.init.xavier_normal_
    torch.nn.init.kaiming_uniform_
4.3 Repeat over certain numbers of epoch

Shuffle whole training data

shuffle
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, **kwargs)
* For each mini-batch data
* load mini-batch data

```
for batch_idx, (data, target) in enumerate(train_loader): \
    ...
```

* compute gradient of loss over parameters

```
 output = net(data) # make prediction
 loss = loss_fn(output, target)  # compute loss
 loss.backward() # compute gradient of loss over parameters
```

* update parameters with gradient descent

```
optimzer.step() # update parameters with gradient descent
```
Can you write Python code for it?

'''step 8'''
def train(train_loader, model, loss_fn, optimizer,device):
    """train model using loss_fn and optimizer. When thid function is called, model trains for one epoch.
    Args:
        train_loader: train data
        model: prediction model
        loss_fn: loss function to judge the distance between target and outputs
        optimizer: optimize the loss function
    Returns:
        total_loss: loss
    """

    # set the module in training model, affecting module e.g., Dropout, BatchNorm, etc.
    model.train()

    total_loss = 0

    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad() # clear gradients of all optimized torch.Tensors'
        outputs = model(data) # make predictions
        loss = loss_fn(outputs, target) # compute loss
        total_loss += loss.item() # accumulate every batch loss in a epoch
        loss.backward() # compute gradient of loss over parameters

        optimizer.step() # update parameters with gradient descent

    average_loss = total_loss / batch_idx # average loss in this epoch

    return average_loss

'''step 9'''
def evaluate(loader, model, loss_fn, device):
    """test model's prediction performance on loader.
    When thid function is called, model is evaluated.
    Args:
        loader: data for evaluation
        model: prediction model
        loss_fn: loss function to judge the distance between target and outputs
    Returns:
        total_loss
        accuracy
    """

    # context-manager that disabled gradient computation
    with torch.no_grad():

        # set the module in evaluation mode
        model.eval()

        correct = 0.0 # account correct amount of data
        total_loss = 0  # account loss

        for batch_idx, (data, target) in enumerate(loader):
            data, target = data.to(device), target.to(device)
            outputs = model(data) # make predictions
 ...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
If you want design a model, in its convolution layers, the original learning rate is 0.1, while the original learning rate of linear layer is 0.01, and you want to nine times smaller its learning rate every 10 epoch. Please write down your solution by pseudo code.
4.4 save model
Pytorch provide two kinds of method to save model. We recommmend the method which only saves parameters. Because it's more feasible and dont' rely on fixed model.
When saving parameters, we not only save learnable parameters in model, but also learnable parameters in optimizer.
A common PyTorch convention is to save models using either a .pt or .pth file extension.
Read more abount save load from this link
Generally speaking, to save our disk space and training time, we usually save the best model in Verification set and the last model for the last epoch.
the next cell will show you how to save the parameters of model and models.
Can you write Python code for it?

# to save the parameters of a model
torch.save(model.state_dict(), './params/resnet18_params.pt')

# to save the model
torch.save(model, './params/resnet18.pt')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
We can see the last layer of convolution is 'layer4.1.bn2.num_batches_tracked'. Now, let's load it to our new model.
Can you write Python code for it?

pretrained_params = torch.load('./params/resnet18_params.pt')
model2_params = model2.state_dict()
for (pretrained_key,pretrained_val), (model2_key,model2_val) in zip(list(pretrained_params.items()),list(model2_params.items())):
    model2_params[model2_key] = pretrained_val
    if model2_key == 'layer4.1.bn2.num_batches_tracked':
        break

# don't forget to load parametes dict to your model!!!
model2.load_state_dict(model2_params)

print(model1.state_dict())

print(model2.state_dict())


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 14 Training Issues 2/TrainingIssues.ipynb".
The first task is:
You can get your the performance of your model at test set in every epoch. so, you can save the best model during the training period. Please add this function into your train() function
5. Transfer Learning
In this section, you will learn how to load the parameters you trained to your new model
Can you write Python code for it?

model1 = torchvision.models.resnet18()
print(model1.state_dict())

# generate a parameters file of model1
torch.save(model1.state_dict(),'./params/resnet18_params.pt')

model2 = torchvision.models.resnet18()
model2.fc = nn.Linear(512,10)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
2.2 Training Clssifier
Before achieving this intersting idea, we need to train a classifier firstly. Here we train a simply fully connected network with best accuracy=93.1% on testing sets.
2.3  Generate Classes Prototype
Regarding to the best accuray=93.1% on testing sets, actually this is a poor performance on MNIST because we only build a fully connected network and do nothing with optimization even no activation function.
Next we will generate  corresponding classes prototype for all categories from digit 0 to digit 9. To do this, we need a few tricks as following:

Initializing input with random noise. Here, we initialize input from standard normal distribution.
Freezing except updating the initial input the parameters of model in backpropagation stage.

To make the distribution of input closer to the distribution of real data, we also calculate the mean squared error between pixel-wise mean for each categories and input as regularization term, which is defined by $z$. Therefore, the loss function is as following:
$$
\begin{equation}
\mathcal{L}=cross_entropy(f(x),y)+\lambda mse_loss(x, z)
\end{equation}
$$
where we use $f(.)$ denote the pretrained model, $x$ denote the input, $y$ denote corresponding label, $cross_entropy$ and $mse_loss$ denote cross entropy error and mean squared error, repectively. Note that we need to set the mode of this model to eval.
Can you write Python code for it?

import torch
from torch.autograd import Variable
from torch import nn, optim

import matplotlib.pyplot as plt
import numpy as np

from class_prototype.model import MNIST_DNN
from utils.utils import set_parameters_static
from utils.utils import get_img_means

lmda = 0.1

class Prototype(nn.Module):
    def __init__(self):
        super(Prototype, self).__init__()

criterion = nn.CrossEntropyLoss()
regular = nn.MSELoss()

model = MNIST_DNN()

model.load_state_dict(torch.load('./class_prototype/MNIST_CNN.pkl'))
# don't update parameters of original model
model = set_parameters_static(model)

# init images and labels:0-9
x_prototype = torch.zeros(10, 784)
y_prototype = torch.linspace(0, 9, 10)
y_prototype = y_prototype.type(torch.LongTensor)

x_prototype = Variable(x_prototype, requires_grad=True)
y_prototype = Variable(y_prototype)

imgs_means = get_img_means('./dataset/mnist')
imgs_means = Variable(imgs_means)

optimizer = optim.Adam([x_prototype], lr=0.01)
print('begin training...')
for i in range(10000):
    optimizer.zero_grad()
    logits_prototype = model(x_prototype)
    cost_protype = criterion(logits_prototype, y_prototype) + lmda * regular(x_prototype, imgs_means)
    cost_protype.backward()
    optimizer.step()
    if i % 500 == 0:
        print('cost_protype={:.6f}'.format(cost_protype.item()))


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
3 Gradient based Methods
In gradient-based algorithms, the gradient of the output with respect to the input is used for constructing the saliency maps. The algorithms in this class differ in the way the gradients are modified during backpropagation. Relevance score based algorithms try to attribute the relevance of each input pixel by backpropagating the probability score instead of the gradient. However, all of these methods involve a single forward and backward pass through the net to generate heatmaps as opposed to multiple forward passes for the perturbation based methods. Evidently, all of these methods are computationally cheaper as well as free of artifacts originating from perturbation techniques.
3.1 Saliency Map
In this section we describe how a classification ConvNet can be queried about the spatial support of
a particular class in a given image. Given an image $I_0$, a class $c$, and a classification ConvNet with
the class score function $S_c(I)$, we would like to rank the pixels of $I_0$ based on their influence on the
score $S_c(I_0)$.
We start with a motivational example. Consider the linear score model for the class $c$:
$$S_c(I) = w^T_cI + b_c$$
where the image I is represented in the vectorised (one-dimensional) form, and wc and bc are respectively the weight vector and the bias of the model. In this case, it is easy to see that the magnitude
of elements of w defines the importance of the corresponding pixels of I for the class $c$.
In the case of deep ConvNets, the class score $S_c(I)$ is a highly non-linear function of $I$, so the
reasoning of the previous paragraph can not be immediately applied. However, given an image
$I_0$, we can approximate $S_c(I)$ with a linear function in the neighbourhood of $I_0$ by computing the
first-order Taylor expansion:
$$Sc(I) ≈ w^TI + b$$
where $w$ is the derivative of $S_c$ with respect to the image $I$ at the point (image) $I_0$:
$$w=\frac{\partial S_c}{\partial I} | I_0$$
3.1.1 Train CNN Classifier
Similarly, we build and train a CNN classifier well....
Can you write Python code for it?

import torch
from torch.autograd import Variable
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch import nn, optim

class MNIST_CNN(nn.Module):
    def __init__(self):
        super(MNIST_CNN, self).__init__()
        self.layer1 = nn.Sequential(nn.Conv2d(1, 32, 3, stride=1, padding=1), nn.ReLU(True),
                                    nn.MaxPool2d(2, 2, padding=1), nn.Dropout(p=0.7))
        self.layer2 = nn.Sequential(nn.Conv2d(32, 64, 3, stride=1, padding=1), nn.ReLU(True),
                                    nn.MaxPool2d(2, 2, padding=1), nn.Dropout(p=0.7))
        self.layer3 = nn.Sequential(nn.Conv2d(64, 128, 3, stride=1, padding=1), nn.ReLU(True),
                                    nn.MaxPool2d(2, 2, padding=1), nn.Dropout(p=0.7))
        self.layer4 = nn.Sequential(nn.Linear(128 * 5 * 5, 625), nn.ReLU(True),
                                    nn.Dropout(0.5))
        self.layer5 = nn.Sequential(nn.Linear(625, 10))

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = x.view(-1, 128 * 5 * 5)
        x = self.layer4(x)
        x = self.layer5(x)
        return x


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
4 Feature activation map
4.1 Class Activation Map(CAM)
CAM revisits the global average pooling layer(GAP), and sheds light on how it explicitly enables
the convolutional neural network to have remarkable localization ability despite being trained on image-level labels.
Given a network architecture similar to ResNet18, where the network largely consists of convolutional layers, and just before the final output layer (softmax in the case of categorization), GAP is performed on the convolutional feature maps and use those as features for a fully-connected layer that produces the desired output (categorical or otherwise).
Given this simple connectivity structure, we can identify the importance of the image regions by projecting back the weights of the output layer on to the convolutional feature maps, a technique we call class activation mapping.
GAP outputs the spatial average of the feature map of each unit at the last convolutional layer. A weighted sum of these values is used to generate the final output. Similarly, we compute a weighted sum of the feature maps of the last convolutional layer to obtain our class activation maps. We describe this more formally below for the case of softmax.
For a given image, let $f_k(x, y)$ represent the activation of unit $k$ in the last convolutional layer at spatial location $(x, y)$. Then, for unit $k$, the result of performing global average pooling, $F_k$ is $\sum _{(x,y)}f_k(x, y)$. Thus, for a given class $c$, the input to the softmax, $S_c$, is $\sum_k w_c^kF_k$ where $w_c^k$ is the weight corresponding to class $c$ for unit $k$. Essentially, $w_c^k$ indicates the importance of $F_k$ for class $c$. Finally the output of the softmax for class $c$, Pc is given by $\frac{exp(S_c)}{ \sum_c exp(S_c)}$. Here we ignore the bias term: we explicitly set the input bias of the softmax to 0 as it has little to no impact on the
classification performance.
By plugging $F_k=\sum {(x,y)}f_k(x, y)$ into the class score, $S_c$, we obtain:
$$
\begin{align}
S_c &= \sum_k w_k^c \sum...
Can you write Python code for it?

# simple implementation of CAM in PyTorch for the networks such as ResNet, DenseNet, SqueezeNet, Inception
import io
import requests
import matplotlib.pyplot as plt
from PIL import Image
from torchvision import models, transforms
from torch.autograd import Variable
from torch.nn import functional as F
import numpy as np
import cv2

# input image
LABELS_URL = 'https://s3.amazonaws.com/outcome-blog/imagenet/labels.json'
IMG_URL = 'http://media.mlive.com/news_impact/photo/9933031-large.jpg'

# networks such as googlenet, resnet, densenet already use global average pooling at the end, so CAM could be used directly.
model_id = 2
if model_id == 1:
    net = models.squeezenet1_1(pretrained=True)
    finalconv_name = 'features' # this is the last conv layer of the network
elif model_id == 2:
    net = models.resnet18(pretrained=True)
    finalconv_name = 'layer4'
elif model_id == 3:
    net = models.densenet161(pretrained=True)
    finalconv_name = 'features'

net.eval()

features_blobs = []
def hook_feature(module, input, output):
    features_blobs.append(output.data.cpu().numpy())

net._modules.get(finalconv_name).register_forward_hook(hook_feature)

# get the softmax weight
params = list(net.parameters())
weight_softmax = np.squeeze(params[-2].data.numpy())

def returnCAM(feature_conv, weight_softmax, class_idx):
    # generate the class activation maps upsample to 256x256
    size_upsample = (256, 256)
    bz, nc, h, w = feature_conv.shape
    output_cam = []
    for _ in class_idx:
        cam = weight_softmax[class_idx].dot(feature_conv.reshape((nc, h*w)))
        cam = cam.reshape(h, w)
        cam = cam - np.min(cam)
        cam_img = cam / np.max(cam)

        cam_img = np.uint8(255 * cam_img)
        output_cam.append(cv2.resize(cam_img, size_upsample))
    return output_cam

normalize = transforms.Normalize(
   mean=[0.485, 0.456, 0.406],
   std=[0.229, 0.224, 0.225]
)
preprocess = transforms.Compose([
   transforms.Resize((224,224)),
   transforms.ToTensor(),
   normalize
])

response =...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
Discussions
In this presentation, we introduct several methods to interpret neural network. We have so far understood both basic algorithms as well as gradient-based methods. Computationally and practically, the gradient-based methods are computationally cheaper and measure the contribution of the pixels in the neighborhood of the original image. For more methods, you can check these techniques as following:

Filter visulization & occlusion experiments & transpose convolution
Guided backpropagation

Assignments
Q1: For class prototype generation, in the above demo, the input is filled with zeros. What you need to do is to initialize the input with digit 8 and generate the target class prototype of digit 3 and 1.
Hints: You can use digit 8 as input, where a typical digit 8 image is saved in dataset/test_imgs/8.png.
Can you write Python code for it?

img = Image.open('./dataset/test_imgs/8.png')
plt.imshow(img)
img.close()


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
Now, we can show the results using plt package.
Can you write Python code for it?

assert gradients.shape == (10, 1, 28, 28)
gradients = np.squeeze(np.square(gradients), 1).reshape(10, 784)

sample_imgs = imgs.data.numpy()
plt.figure(figsize=(7, 7))
for i in range(5):
    plt.subplot(5, 2, 2 * i +1)
    plt.imshow(np.reshape(sample_imgs[2 * i,:], (28, 28)), cmap='gray')
    plt.imshow(np.reshape(gradients[2 * i,:], (28, 28)), cmap='hot', alpha=0.5)
    plt.title('Digit:{}'.format(2 * i))

    plt.subplot(5, 2, 2 * i + 2)
    plt.imshow(np.reshape(sample_imgs[2 *i + 1, :], (28, 28)), cmap='gray')
    plt.imshow(np.reshape(gradients[2*i + 1, :], (28, 28)), cmap='hot', alpha=0.5)
    plt.title('Digit:{}'.format(2 * i + 1))

plt.tight_layout()
plt.show()


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
Q2: In the visulization of CAM, the class activation map for class $c$, $M_c$, is calculated by the weighted($w^c$) product of all feature maps(512 feature maps in ResNet18) in last convolutional layer. Could you use the 20 feature maps with max activation value to visulize CNNs？
Hints:
1. You can use arithmetic mean to indicate the activation value for one feature map.
2. A recommendable way is to modify the function returnCAM.
3. You need to have a clear understanding for how to index and slice numpy arrays.
Can you write Python code for it?

def returnCAM(feature_conv, weight_softmax, class_idx):
    # generate the class activation maps upsample to 256x256
    size_upsample = (256, 256)
    bz, nc, h, w = feature_conv.shape
    output_cam = []
    for _ in class_idx:
        features = feature_conv.reshape((nc, h*w))
        index = np.argsort(-np.array([np.mean(features[i]) for i in range(nc)]))
        cam = weight_softmax[class_idx, index[:20]].dot(features[index[:20]])
        cam = cam.reshape(h, w)
        cam = cam - np.min(cam)
        cam_img = cam / np.max(cam)

        cam_img = np.uint8(255 * cam_img)
        output_cam.append(cv2.resize(cam_img, size_upsample))
    return output_cam

img = cv2.imread('./cam_based/test.jpg')
height, width, _ = img.shape
CAMs = returnCAM(features_blobs[0], weight_softmax, [idx[0].item()])

heatmap = cv2.applyColorMap(cv2.resize(CAMs[0],(width, height)), cv2.COLORMAP_JET)
result = heatmap * 0.3 + img * 0.5
cv2.imwrite('./cam_based/CAM.jpg', result)

plt.imshow(Image.open('./cam_based/CAM.jpg'))
plt.axis('off')
plt.show()


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 16 Visualization/pre.ipynb".
The first task is:
We use pretrained weights to initialize this classifier. Then freeze the model parameters and only train input tensor using Adam optimizer, where the loss decrease stably until 0.0001. After training for 10000 steps, we can show class prototype using plot package as following.
Can you write Python code for it?

x_prototype = x_prototype.data.numpy()

assert x_prototype.shape == (10, 784)
plt.figure(figsize=(7, 7))
for i in range(5):
    left = x_prototype[2*i,:].reshape(28, 28)
    plt.subplot(5, 2, 2 * i +1)
    plt.imshow(left, cmap='gray', interpolation='none')
    plt.title('Digit:{}'.format(2 * i))
    plt.colorbar()

    right = x_prototype[2*i+1,:].reshape(28, 28)
    plt.subplot(5, 2, 2 * i + 2)
    plt.imshow(right, cmap='gray', interpolation='none')
    plt.title('Digit:{}'.format(2 * i + 1))
    plt.colorbar()

plt.tight_layout()
plt.show()


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Call this function, you can get a well trained classifier with accuracy up to 95+%.

Or you can use the pretrained one.
Can you write Python code for it?

# raw_train()
# Call this function, you can get a well trained classifier with accuracy up to 95+%.


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Please finish the `black_box_attack` method.

Hints:
You need to generate adversarial examples by the attacking model(pretrained lenet), and then use the
adversarial examples to test the robustness of the defending model (raw resnet or adversarially trained resnet).
Can you write Python code for it?

def black_box_attack(attacker, defender, device, test_loader, epsilon):
    '''
    Use attacker model to generate adversarial examples.
    And try to test the robustness of the defender.
    '''
    # Accuracy counter
    correct = 0
    adv_examples = []

    # Loop over all examples in test set
    for data, target in test_loader:

        # Send the data and label to the device
        data, target = data.to(device), target.to(device)
        # Set requires_grad attribute of tensor. Important for Attack
        data.requires_grad = True

        # Forward pass the data through the **attacker**

        # ********* Code here ****************
        output = attacker(data) # attacker or defender

        init_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability

        # If the initial prediction is wrong, dont bother attacking, just move on
        if init_pred.item() != target.item():
            continue

        # Calculate the loss
        loss = F.nll_loss(output, target)

        # Zero all existing gradients of the attacker model
        attacker.zero_grad()

        # Calculate gradients of model in backward pass
        loss.backward()

        # Collect datagrad
        data_grad = data.grad.data

        # ********* Code here ****************
        # Call FGSM Attack
        perturbed_data = fgsm_attack(data, epsilon, torch.sign(data_grad))  # we already have data, epsilon and data_grad now.

        # Use the defender to classify the perturbed image
        output = defender(perturbed_data) # attacker or defender ?

        # Check for success
        final_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability
        if final_pred.item() == target.item():
            correct += 1
            # Special case for saving 0 epsilon examples
            if (epsilon == 0) and (len(adv_examples) < 5):
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
               ...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Please use the `black_box_attack_test` method to test the adversarially trained resnet.

Can you write Python code for it?

adv_accs, adv_exs = black_box_attack_test(pretrained_lenet, adv_resnet)

print('Blue: Raw ResNet attacked by LeNet.')
print('Red: Robust ResNet attacked by LeNet.')
plt_comparion(epsilons, [raw_accs, adv_accs])


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Please use the `black_box_attack_test` method to test the raw resnet model.

Can you write Python code for it?

raw_accs, raw_exs = black_box_attack_test(pretrained_lenet, raw_resnet)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Repeat the test function and plot the figure of acc v.s. eps. and examples.

Can you write Python code for it?

re_accuracies, re_examples = attack_test(adv_resnet)
# Epsilon    Accuracy
#    0         ~raw
#   0.05       <= ~98%
#   0.15       <= ~98%
#   0.25       <= ~95%

plt_acc_vs_eps(epsilons, re_accuracies)

plt_examples(epsilons, re_examples)

def plt_comparion(epsilons, accs):
    plt.figure(figsize=(5,5))
    plt.plot(epsilons, accs[0], "*-")
    plt.plot(epsilons, accs[1], "*-", c='red')
    plt.yticks(np.arange(0, 1.1, step=0.1))
    plt.xticks(np.arange(0, .35, step=0.05))
    plt.title("Accuracy vs Epsilon")
    plt.xlabel("Epsilon")
    plt.ylabel("Accuracy")
    plt.show()

print('Blue: Raw ResNet under white-box attack.')
print('Red : Robust ResNet under white-box attack.')
plt_comparion(epsilons, [accuracies, re_accuracies])


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:

Run the attack function here.

Can you write Python code for it?

# Remember we have saved the model at 'raw_resnet_path'.
pretrained_model = raw_resnet_path
accuracies, examples = attack_test(pretrained_model)
# Hints: below are the accuracies under FGSM attack with different epsilons.
# Epsilon    Accuracy
#    0         ~raw
#   0.05       < 60%
#   0.15       < 30%
#   0.25       < 10%


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Defense
The most popular defense in current research papers is probably adversarial training.
Adversarial Training
The idea is to inject adversarial examples into training process and train the
model either on adversarial examples or on mix of clean and adversarial examples.
Here, we use the training model to generate adversarial examples by FGSM attack and then
feed them into the model.
The perturbed image is get by this equation:
\begin{align}perturbed_image = image + epsilon*sign(data_grad) = x + \epsilon * sign(\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y))\end{align}
We already have the raw image, and we can get the grad by one forward and backward pass. What we need here is
epsilon. Since we need it to train a more robust model, we set epsilon equals to 0.3. You can try different epsilon and see what happens.

Please try to finish the `fgsm_train` function.

Can you write Python code for it?

def fgsm_train(model, device, train_loader, optimizer, criterion, epoch, epsilon=0.3):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        data.requires_grad = True

        #****** To get the grad  *********
        # Forward pass
        output = model(data)
        loss = criterion(output, target)
        optimizer.zero_grad()
        # Backward pass
        loss.backward()
        # Collect datagrad
        sign_data_grad = torch.sign(data.grad.data)

        #****** Get perturbed_data  *********
        # Code here
        perturbed_data = data + epsilon * sign_data_grad

        # Note: Adding clipping to maintain [0,1] range
        perturbed_data.clamp_(0.0, 1.0)

        #****** Train model with perturbed_data  *********
        optimizer.zero_grad()
        pert_output = model(perturbed_data)
        pert_loss = criterion(pert_output, target)
        pert_loss.backward()
        optimizer.step()

        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), pert_loss.item()))

def adv_main(save_name='./data/fgsm_adv_mnist_cnn.pt'):
    train_loader, test_loader = dataset_mnist()
    # Initialize the network
    model = resnet.ResNet18().to(device)

    # Define the optimizer
    optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
    criterion = nn.CrossEntropyLoss()

    for epoch in range(1, epochs+1):
        fgsm_train(model, device, train_loader, optimizer,criterion, epoch)
        test(model, device, criterion, test_loader)

    # Save model
    torch.save(model.state_dict(), save_name)
    print('Model saved.')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
FGSM Attack
Now, we can define the function that creates the adversarial examples by
perturbing the original inputs. The fgsm_attack function takes three
inputs, image is the original clean image ($x$), epsilon is
the pixel-wise perturbation amount ($\epsilon$), and data_grad
is gradient of the loss w.r.t the input image
($\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y)$). The function
then creates perturbed image as
\begin{align}perturbed_image = image + epsilon*sign(data_grad) = x + \epsilon * sign(\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y))\end{align}
Finally, in order to maintain the original range of the data, the
perturbed image is clipped to range $[0,1]$.

Please try to finish the attack code with the information above.

Can you write Python code for it?

# FGSM attack code
def fgsm_attack(image, epsilon, data_grad):
    # Collect the element-wise sign of the data gradient
    sign_data_grad = data_grad.sign()
    # Create the perturbed image by adjusting each pixel of the input image

    # code here
    perturbed_image = image + epsilon * sign_data_grad

    # Note: Adding clipping to maintain [0,1] range
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    # Return the perturbed image
    return perturbed_image


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Implementation
Int this section, we will discuss the input parameters for the tutorial,
define the model under attack, then code the attack and run some tests.
Inputs
There are only three inputs for this tutorial, and are defined as
follows:
-  epsilons - List of epsilon values to use for the run. It is
   important to keep 0 in the list because it represents the model
   performance on the original test set. Also, intuitively we would
   expect the larger the epsilon, the more noticeable the perturbations
   but the more effective the attack in terms of degrading model
   accuracy. Since the data range here in $[0,1]$, no epsilon
   value should exceed 1.
-  pretrained_model - Path to the pretrained MINST model which was
   trained with resnet (source code located at resnet.py) and lenet.
-  use_cuda - boolean flag to use CUDA if desired and available.
   Note, a GPU with CUDA is not critical for this tutorial as a CPU will
   not take much time.

Please try to detect available gpu device with torch.cuda

Can you write Python code for it?

epsilons = [0, .05, .15, .25]
# epsilons = [0, .05, .1, .15, .2, .25, .3]

# Use pretrained model or not
# pretrained_model = "data/mnist_cnn.pt"

# Both of the network and dataset are simple, so we can use small number of epochs.
epochs = 3

use_cuda = True
# Define what device we are using
# Code here
print("CUDA Available: ", torch.cuda.is_available())
# hint: torch.cuda.is_available()

device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")
# device = torch.device("cuda" if (use_cuda and 'code' here) else "cpu")


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Load Dataset
To accelerate training process, we use the raw test dataset as our training dataset.
Also, you can train model with the original training dataset.
Can you write Python code for it?

# MNIST dataloader
def dataset_mnist(train_batch=32, test_batch=1):
    # MNIST Train dataset
#     train_loader = torch.utils.data.DataLoader(
#         datasets.MNIST('./data/mnist', train=True, download=False,
#                        transform=transforms.Compose([transforms.ToTensor()])),
#             batch_size=train_batch, shuffle=True)
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('./data/mnist', train=False, download=True,
                       transform=transforms.Compose([transforms.ToTensor()])),
            batch_size=train_batch, shuffle=True)

    # MNIST Test dataset and dataloader declaration
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('./data/mnist', train=False, download=True,
                       transform=transforms.Compose([transforms.ToTensor()])),
            batch_size=test_batch, shuffle=False)
    return train_loader, test_loader


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Model Under Attack
As mentioned, the model under attack is the same MNIST model from resnet.py. You may
train and save your own MINST model or you can use the provided model.
The Net definition and test dataloader here have benn copied from
the MNIST example. The purpose of this section is to define the
model and dataloader, then initialize the model and load the pretrained
weights.
Can you write Python code for it?

# Train a traditional classifier with resnet or use the pretrained one.
raw_resnet_path = './data/mnist_resnet.pt'


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Run Attack
The last part of the implementation is to actually run the attack. Here,
we run a full test step for each epsilon value in the epsilons input.
For each epsilon we also save the final accuracy and some successful
adversarial examples to be plotted in the coming sections. Notice how
the printed accuracies decrease as the epsilon value increases. Also,
note the $\epsilon=0$ case represents the original test accuracy,
with no attack.
Can you write Python code for it?

def attack_test(model_path):
    model = resnet.ResNet18().to(device)
    # Load the pretrained model
    print('Pretrained Model', model_path)
    model.load_state_dict(torch.load(model_path, map_location='cpu'))
    # Set the model in evaluation mode. In this case this is for the Dropout layers
    model.eval()

    _, test_loader = dataset_mnist()

    accuracies = []
    examples = []

    # Run test for each epsilon
    for eps in epsilons:
        acc, ex = adv_test(model, device, test_loader, eps)
        accuracies.append(acc)
        examples.append(ex)
    return accuracies, examples


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Sample Adversarial Examples
Remember the idea of no free lunch? In this case, as epsilon increases
the test accuracy decreases BUT the perturbations become more easily
perceptible. In reality, there is a tradeoff between accuracy
degredation and perceptibility that an attacker must consider. Here, we
show some examples of successful adversarial examples at each epsilon
value. Each row of the plot shows a different epsilon value. The first
row is the $\epsilon=0$ examples which represent the original
“clean” images with no perturbation. The title of each image shows the
“original classification -> adversarial classification.” Notice, the
perturbations start to become evident at $\epsilon=0.15$ and are
quite evident at $\epsilon=0.3$. However, in all cases humans are
still capable of identifying the correct class despite the added noise.
Can you write Python code for it?

def plt_examples(epsilons, examples):
    # Plot several examples of adversarial samples at each epsilon
    cnt = 0
    plt.figure(figsize=(8,10))
    for i in range(len(epsilons)):
        for j in range(len(examples[i])):
            cnt += 1
            plt.subplot(len(epsilons),len(examples[0]),cnt)
            plt.xticks([], [])
            plt.yticks([], [])
            if j == 0:
                plt.ylabel("Eps: {}".format(epsilons[i]), fontsize=14)
            orig,adv,ex = examples[i][j]
            plt.title("{} -> {}".format(orig, adv))
            plt.imshow(ex, cmap="gray")
    plt.tight_layout()
    plt.show()

plt_examples(epsilons, examples)


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 17 Robustness/Robustness.ipynb".
The first task is:
Testing Function
Finally, the central result of this tutorial comes from the test
function. Each call to this test function performs a full test step on
the MNIST test set and reports a final accuracy. However, notice that
this function also takes an epsilon input. This is because the
test function reports the accuracy of a model that is under attack
from an adversary with strength $\epsilon$. More specifically, for
each sample in the test set, the function computes the gradient of the
loss w.r.t the input data ($data_grad$), creates a perturbed
image with fgsm_attack ($perturbed_data$), then checks to see
if the perturbed example is adversarial. In addition to testing the
accuracy of the model, the function also saves and returns some
successful adversarial examples to be visualized later.

Please try to finish the test code with `fgsm_attack` function.

Can you write Python code for it?

def adv_test( model, device, test_loader, epsilon ):

    # Accuracy counter
    correct = 0
    adv_examples = []

    # Loop over all examples in test set
    for data, target in test_loader:

        # Send the data and label to the device
        data, target = data.to(device), target.to(device)
        # Set requires_grad attribute of tensor. Important for Attack
        data.requires_grad = True

        # Forward pass the data through the model
        output = model(data)
        init_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability

        # If the initial prediction is wrong, dont bother attacking, just move on
        if init_pred.item() != target.item():
            continue

        # Calculate the loss
        loss = F.nll_loss(output, target)

        # Zero all existing gradients
        model.zero_grad()

        # Calculate gradients of model in backward pass
        loss.backward()

        # Collect datagrad
        data_grad = data.grad.data

        # Call FGSM Attack
        # code here
        perturbed_data = fgsm_attack(data, epsilon, torch.sign(data_grad))

        # Re-classify the perturbed image
        output = model(perturbed_data)

        # Check for success
        final_pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability
        if final_pred.item() == target.item():
            correct += 1
            # Special case for saving 0 epsilon examples
            if (epsilon == 0) and (len(adv_examples) < 5):
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
                adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
        else:
            # Save some adv examples for visualization later
            if len(adv_examples) < 5:
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
                adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )

    # Calculate final accuracy for this...


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 3 Backpropagation/week_3_numpy.ipynb".
The first task is:
!!! Homework 1.   Describe the training procedure, based on codes above.
BP神经网络的训练流程主要分为神经网络的初始化、信号的前向传播、误差的反向传播、权重的更新四个步骤。

神经网络的初始化：初始化输入层、隐层、输出层的节点，同时初始化各层之间的权重，选择激励函数、学习速率等参数。这里各层节点分别为3、5、2，权重固定一个值，而不是均匀随机生成，激励函数选择Sigmoid函数。
信号的前向传播：输入训练集样例，通过权重的选择，以及激励函数的作用，最终得到一个输出值。
误差的反向传播：比较输出值和训练集样例的标签，计算出误差，并逐步反向传播至隐层、输入层。
权重的更新：根据下一层传回的误差和学习速率，对当前权重进行更新。

BP神经网络的训练首先初始化神经网络，然后循环训练代数次。
在训练循环中，会根据预先选择的梯度下降法对数据进行分割，每份数据都依次进行前向传播、反向传播、更新权重过程。其中，梯度下降将不分割数据，而随机梯度下降法每次只使用一个样例，mini批梯度下降法根据参数决定每份数据的样例数。
训练结束后，可使用测试集样例来检测当前BP神经网络的正确率。
Can you write Python code for it?

# Draw losses curve using losses
show_curve(losses, 'Loss')

# Draw Accuracy curve using accuracies
show_curve(accuracies, 'Accuracy')


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 3 Backpropagation/week_3_numpy.ipynb".
The first task is:
1.2 interpretation of the gradient
Interpretation:The derivatives indicate the rate of change of a function with respect to that variable surrounding an infinitesimally small region near a particular point:
$$\frac{df(x)}{dx} = \lim_{h\ \to 0} \frac{f(x + h) - f(x)}{h}$$
In other words, the derivative on each variable tells you the sensitivity of the whole expression on its value.As mentioned, the gradient $\nabla f$ is the vector of partial derivatives, so we have that $\nabla f = [\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}] = [y, x]$.
2. Compound expressions with chain rule
2.1 Simple examples for chain rule
Lets now start to consider more complicated expressions that involve multiple composed functions, such as $f(x,y,z) = (x + y) z$.
This expression is still simple enough to differentiate directly, but we’ll take a particular approach to it that will be helpful with understanding the intuition behind backpropagation.
In particular, note that this expression can be broken down into two expressions: $q=x+y$ and $f=qz$. As seen in the previous section,$f$ is just multiplication of $q$ and $z$, so $\frac{\partial f}{\partial q} = z, \frac{\partial f}{\partial z} = q$,and $q$ is addition of $x$ and $y$ so $\frac{\partial q}{\partial x} = 1, \frac{\partial q}{\partial y} = 1$.
However, we don’t necessarily care about the gradient on the intermediate value $q$ - the value of $\frac{\partial f}{\partial q}$ is not useful. Instead, we are ultimately interested in the gradient of $f$ with respect to its inputs $x$,$y$,$z$.
The chain rule tells us that the correct way to “chain” these gradient expressions together is through multiplication. For example, $\frac{\partial f}{\partial x} = \frac{\partial f}{\partial q} \frac{\partial q}{\partial x}$. In practice this is simply a multiplication of the two numbers that hold the two gradients. Lets see this with an example:
Can you write Python code for it?

# set some inputs
x = -2; y = 5; z = -4

# perform the forward pass
q = 2*x + y # q becomes 1
f = q * z # f becomes -4
print(q, f)

# perform the backward pass (backpropagation) in reverse order:
# first backprop through f = q * z = (2*x+y) * z
dfdz = q # df/dz = q, so gradient on z becomes 3
dfdq = z # df/dq = z, so gradient on q becomes -4
# now backprop through q = x + y
dfdx = 2.0 * dfdq # dq/dx = 1. And the multiplication here is the chain rule!
dfdy = 1.0 * dfdq # dq/dy = 1
print('df/dx is {:2}'.format(dfdx))
print('df/dy is {:2}'.format(dfdy))


I am working on the file "Homework/Principles of Artificial Neural Networks/Week 3 Backpropagation/week_3_numpy.ipynb".
The first task is:
3.5 Training model and make predictions
Can you write Python code for it?

learning_rate = 0.1

epochs = 400 # training epoch

batch_size = len(train_data) # GD
# batch_size = 1               # SGD
# batch_size = 8               # mini-batch

model = FeedForward_Neural_Network(learning_rate) # declare a simple feedforward neural model

losses = []
accuracies = []

for i in range(epochs):
    loss = 0
    for index, (xs, ys) in enumerate(generate_batch(train_data, train_labels_onehot, batch_size)):
        predictions = model.forward(xs) # forward phase
        loss += 1/2 * np.mean(np.sum(np.square(ys-predictions), axis=1)) # Mean square error
        model.backward(xs, ys, predictions) # backward phase

    losses.append(loss)

    # train dataset acc computation
    predictions = model.forward(train_data)
    # compute acc on train dataset
    accuracy = get_accuracy(predictions, train_labels_onehot)
    accuracies.append(accuracy)

    if i % 50 == 0:
        print('Epoch: {}, has {} iterations'.format(i, index+1))
        print('\tLoss: {:.4f}, \tAccuracy: {:.4f}'.format(loss, accuracy))

test_predictions = model.forward(test_data)
# compute acc on test dataset
test_accuracy = get_accuracy(test_predictions, test_labels_onehot)
print('Test Accuracy: {:.4f}'.format(test_accuracy))