Полносвязная нейронная сеть

В данном домашнем задании вы подготовите свою реализацию полносвязной нейронной сети и обучите классификатор на датасете CIFAR-10


In [ ]:
import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'


def rel_error(x, y):
    """ returns relative error """
    return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))

In [ ]:
class TwoLayerNet(object):
    
    def __init__(self, input_size, hidden_size, output_size, std=1e-4):
        """
        W1: Первый слой с размерами (D, H)
        b1: Вектор байесов, размер  (H,)
        W2: Второй слой с размерами (H, C)
        b2: Вектор байесов, размер (C,)

        Входные парамерты:
        - input_size: Размерность входных данных
        - hidden_size: Размер скрытого слоя
        - output_size: Количество классов
        """
        self.params = {}
        self.params['W1'] = std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)
        
    def loss(self, X, y=None, reg=0.0):
        """
        Вычисление функции потерь

        Входные парамерты:
        - X: Таблица данных (N, D). X[i] - один пример
        - y: Вектор лейблов. Если отсутсвует, то возвращается предсказание лейблов
        - reg: Коэффициент регуляризации

        Возвращает:
        Если y == None, то возвращаются скор для классов

        Если y != None, то возвращаются:
        - Лосс для данного семпла данных
        - grads: Словарь градиентов, ключи соответствуют ключам словаря self.params.
        """
        W1, b1 = self.params['W1'], self.params['b1']
        W2, b2 = self.params['W2'], self.params['b2']
        N, D = X.shape

        scores = None
        #############################################################################
        # TODO: Расчет forward pass или прямой проход, для данных находятся скоры,  #
        # на выходе размер (N, C)                                                   #
        #############################################################################
        pass
        #############################################################################
        #                              END OF YOUR CODE                             #
        #############################################################################

        # Если y == None, то завершаем вызов
        if y is None:
            return scores

        loss = None
        #############################################################################
        # TODO: Расчет Softmax loss для полученных скоров обьектов, на выходе скаляр        #
        #############################################################################
        pass
        #############################################################################
        #                              END OF YOUR CODE                             #
        #############################################################################

        grads = {}
        #############################################################################
        # TODO: Расчет обратнохо прохода или backward pass, находятся градиенты для всех    #
        # параметров, результаты сохраняются в grads, например grads['W1']               #
        #############################################################################
        pass
        #############################################################################
        #                              END OF YOUR CODE                             #
        #############################################################################

        return loss, grads
    

    def train(self, X, y, X_val, y_val,
                learning_rate=1e-3, learning_rate_decay=0.95,
                reg=5e-6, num_iters=100,
                batch_size=200, verbose=False):
        """
        Обучение нейронной сети с помощью SGD

        Входные парамерты:
        - X: Матрица данных (N, D)
        - y: Вектор лейблов (N, )
        - X_val: Данные для валидации (N_val, D)
        - y_val: Вектор лейблов валидации (N_val, )
        - reg: Коэффициент регуляризации
        - num_iters: Количнство итераций
        - batch_size: Размер семпла данных, на 1 шаг алгоритма
        - verbose: Вывод прогресса
        """
        num_train = X.shape[0]
        iterations_per_epoch = max(num_train / batch_size, 1)

        loss_history = []
        train_acc_history = []
        val_acc_history = []

        for it in range(num_iters):
            X_batch = None
            y_batch = None

            #########################################################################
            # TODO: Семпл данных их X-> X_batch, y_batch                              #
            #########################################################################
            pass
            #########################################################################
            #                             END OF YOUR CODE                          #
            #########################################################################
 
            loss, grads = self.loss(X_batch, y=y_batch, reg=reg)
            loss_history.append(loss)

            #########################################################################
            # TODO: Используя градиенты из grads обновите параметры сети                 #
            #########################################################################
            pass
            #########################################################################
            #                             END OF YOUR CODE                          #
            #########################################################################

            if verbose and it % 100 == 0:
                print('iteration %d / %d: loss %f' % (it, num_iters, loss))

            if it % iterations_per_epoch == 0:
                train_acc = (self.predict(X_batch) == y_batch).mean()
                val_acc = (self.predict(X_val) == y_val).mean()
                train_acc_history.append(train_acc)
                val_acc_history.append(val_acc)
                
            # Decay learning rate
            learning_rate *= learning_rate_decay

        return {
          'loss_history': loss_history,
          'train_acc_history': train_acc_history,
          'val_acc_history': val_acc_history,
        }

    def predict(self, X):
        """
        Входные параметры:
        - X: Матрица данных (N, D)

        Возвращает:
        - y_pred: Вектор предсказаний классов для обьектов (N,)
        """
        y_pred = None

        ###########################################################################
        # TODO: Предсказание классов для обьектов из X                                     #
        ###########################################################################
        pass
        ###########################################################################
        #                              END OF YOUR CODE                           #
        ###########################################################################

        return y_pred

In [ ]:
# Инициализация простого примера. Данные и обьект модели

input_size = 4
hidden_size = 10
num_classes = 3
num_inputs = 5

def init_toy_model():
    np.random.seed(0)
    return TwoLayerNet(input_size, hidden_size, num_classes, std=1e-1)

def init_toy_data():
    np.random.seed(1)
    X = 10 * np.random.randn(num_inputs, input_size)
    y = np.array([0, 1, 2, 2, 1])
    return X, y

net = init_toy_model()
X, y = init_toy_data()

Прямой проход, скор

Реализуйте прямой проход в TwoLayerNet.loss


In [ ]:
scores = net.loss(X)
print('Your scores:')
print(scores)
print()
print('correct scores:')
correct_scores = np.asarray([
  [-0.81233741, -1.27654624, -0.70335995],
  [-0.17129677, -1.18803311, -0.47310444],
  [-0.51590475, -1.01354314, -0.8504215 ],
  [-0.15419291, -0.48629638, -0.52901952],
  [-0.00618733, -0.12435261, -0.15226949]])
print(correct_scores)
print()

# The difference should be very small. We get < 1e-7
print('Difference between your scores and correct scores:')
print(np.sum(np.abs(scores - correct_scores)))

Прямой проход, функция потерь

В том же методе реализуйте вычисление функции потерь


In [ ]:
loss, _ = net.loss(X, y, reg=0.05)
correct_loss = 1.30378789133

# Ошибка должна быть < 1e-12
print('Difference between your loss and correct loss:')
print(np.sum(np.abs(loss - correct_loss)))

Обратный проход

Закончите реализацию метода, вычислением градиентов для W1, b1, W2, и b2


In [ ]:
def eval_numerical_gradient(f, x, verbose=True, h=0.00001):
    
    fx = f(x) # evaluate function value at original point
    grad = np.zeros_like(x)
    # iterate over all indexes in x
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:

        # evaluate function at x+h
        ix = it.multi_index
        oldval = x[ix]
        x[ix] = oldval + h # increment by h
        fxph = f(x) # evalute f(x + h)
        x[ix] = oldval - h
        fxmh = f(x) # evaluate f(x - h)
        x[ix] = oldval # restore

        # compute the partial derivative with centered formula
        grad[ix] = (fxph - fxmh) / (2 * h) # the slope
        if verbose:
            print(ix, grad[ix])
        it.iternext() # step to next dimension

    return grad

In [ ]:
loss, grads = net.loss(X, y, reg=0.05)

# Ошибка должна быть меньше или около 1e-8
for param_name in grads:
    f = lambda W: net.loss(X, y, reg=0.05)[0]
    param_grad_num = eval_numerical_gradient(f, net.params[param_name], verbose=False)
    print('%s max relative error: %e' % (param_name, rel_error(param_grad_num, grads[param_name])))

Обучение сети

Реализуйте метод TwoLayerNet.train и метод TwoLayerNet.predict

После того как закончите, обучите сеть на игрушечных данных, которые мы сгенерировали выше, лосс дольжен быть менее или около 0.2.


In [ ]:
net = init_toy_model()
stats = net.train(X, y, X, y,
            learning_rate=1e-1, reg=5e-6,
            num_iters=100, verbose=False)

print('Final training loss: ', stats['loss_history'][-1])

plt.plot(stats['loss_history'])
plt.xlabel('iteration')
plt.ylabel('training loss')
plt.title('Training Loss history')
plt.show()

CIFAR-10


In [ ]:
from keras.datasets import cifar10

In [ ]:
(X_train, y_train), (X_val, y_val) = cifar10.load_data()

X_test, y_test = X_val[:int(X_val.shape[0]*0.5)], y_val[:int(X_val.shape[0]*0.5)]
X_val, y_val = X_val[int(X_val.shape[0]*0.5):], y_val[int(X_val.shape[0]*0.5):]

print('Train data shape: ', X_train.shape)
print('Train labels shape: ', y_train.shape)
print('Validation data shape: ', X_val.shape)
print('Validation labels shape: ', y_val.shape)
print('Test data shape: ', X_test.shape)
print('Test labels shape: ', y_test.shape)

Обучение сети

Обучите сеть на данных CIFAR-10


In [ ]:
input_size = 32 * 32 * 3
hidden_size = 50
num_classes = 10
net = TwoLayerNet(input_size, hidden_size, num_classes)

stats = net.train(X_train, y_train, X_val, y_val,
            num_iters=1000, batch_size=200,
            learning_rate=1e-4, learning_rate_decay=0.95,
            reg=0.25, verbose=True)

val_acc = (net.predict(X_val) == y_val).mean()
print('Validation accuracy: ', val_acc)

Дебаггинг процесса обучния


In [ ]:
plt.subplot(2, 1, 1)
plt.plot(stats['loss_history'])
plt.title('Loss history')
plt.xlabel('Iteration')
plt.ylabel('Loss')

plt.subplot(2, 1, 2)
plt.plot(stats['train_acc_history'], label='train')
plt.plot(stats['val_acc_history'], label='val')
plt.title('Classification accuracy history')
plt.xlabel('Epoch')
plt.ylabel('Clasification accuracy')
plt.show()

Настройка гиперпараметров


In [ ]:
best_net = None

#################################################################################
# TODO: Напишите свою реализцию кросс валидации для настройки гиперпараметров сети       #
#################################################################################
pass
#################################################################################
#                               END OF YOUR CODE                                #
#################################################################################

Проверка качества

С оптимальными гиперпараметрами сеть должна выдавать точнов около 48%.


In [ ]:
test_acc = (best_net.predict(X_test) == y_test).mean()
print('Test accuracy: ', test_acc)