Машинное обучение 101 Все алгоритмы в питоне (нейронная сеть прямого и обратного распространения с нуля)

Делаем задания профессора Эндрю на популярном курсе машинного обучения Coursera на Python.

В этом блоге я расскажу вам о выполнении самых популярных заданий курса машинного обучения, доступных в Интернете, представленных профессором Эндрю Нг. Мы собираемся построить алгоритм нейронной сети с нуля для выполнения мультиклассовой классификации.

Если вы не читали мои предыдущие блоги о машинном обучении 101, вы можете прочитать их здесь.

Введение

В этом блоге мы реализуем алгоритм обратного распространения ошибки для нейронных сетей и применим его к задаче распознавания рукописных цифр. Прежде чем приступить к упражнению по программированию, мы настоятельно рекомендуем посмотреть видеолекции и ответить на контрольные вопросы по соответствующим темам.

Прежде чем мы начнем с блога, нам нужно импортировать все библиотеки, необходимые для этого упражнения по программированию. На протяжении всего курса мы будем использовать numpy для всех операций с массивами и матрицами, matplotlib для построения графиков и scipy для функций и инструментов научных и числовых вычислений.

Импорт пакетов Python

# used for manipulating directory paths
import os

# Scientific and vector computation for python
import numpy as np

# Plotting library
from matplotlib import pyplot

# Optimization module in scipy
from scipy import optimize

# will be used to load MATLAB mat datafile format
from scipy.io import loadmat

# library written for this exercise providing additional functions for assignment submission, and others
import utils

# tells matplotlib to embed plots within the notebook
%matplotlib inline

Нейронные сети

В предыдущем блоге мы внедрили упреждающее распространение для нейронных сетей и использовали его для предсказания рукописных цифр с предоставленными нами весами. В этом блоге мы реализуем алгоритм обратного распространения, чтобы узнать параметры нейронной сети.

загрузка набора данных

#  training data stored in arrays X, y
data = loadmat(os.path.join('Data', 'ex4data1.mat'))
X, y = data['X'], data['y'].ravel()

# set the zero digit to 0, rather than its mapped 10 in this dataset
# This is an artifact due to the fact that this dataset was used in 
# MATLAB where there is no index 0
y[y == 10] = 0

# Number of training examples
m = y.size

Визуализация данных

Мы начнем с визуализации подмножества обучающего набора, используя функцию displayData, ту же функцию, которую мы использовали в предыдущем блоге. Он также содержится в файле utils.py для этого задания. Набор данных тот же, что вы использовали в предыдущем блоге.

Проверьте этот репозиторий GitHub, чтобы найти используемый набор данных и блокнот Jupyter.

В ex4data1.mat есть 5000 обучающих примеров, где каждый обучающий пример представляет собой изображение цифры в градациях серого размером 20 на 20 пикселей. Каждый пиксель представлен числом с плавающей запятой, указывающим интенсивность оттенков серого в этом месте. Сетка пикселей 20 на 20 «разворачивается» в 400-мерный вектор. Каждый из этих обучающих примеров становится отдельной строкой в ​​нашей матрице данных X. Это дает нам матрицу 5000 на 400 X, где каждая строка является обучающим примером для изображения рукописной цифры.

Вторая часть обучающего набора — это 5000-мерный вектор y, который содержит метки для обучающего набора. Следующая ячейка случайным образом выбирает 100 изображений из набора данных и отображает их.

# Randomly select 100 data points to display
rand_indices = np.random.choice(m, 100, replace=False)
sel = X[rand_indices, :]

utils.displayData(sel)

Представление модели

Наша нейронная сеть показана на следующем рисунке.

Он имеет 3 слоя — входной слой, скрытый слой и выходной слой. Напомним, что наши входные данные представляют собой значения пикселей цифровых изображений. Поскольку изображения имеют размер 20 x 20, это дает нам 400 единиц входного слоя (не считая дополнительной единицы смещения, которая всегда выводит +1). Данные обучения были загружены в переменные X и y выше.

Вам предоставлен уже обученный нами набор сетевых параметров Theta_1, Theta_2. Они хранятся в ex4weights.mat и будут загружены в следующую ячейку этой записной книжки в Theta1 и Theta2. Параметры имеют размеры, соответствующие размеру нейронной сети с 25 единицами во втором слое и 10 единицами вывода (соответствующими 10-значным классам).

# Setup the parameters you will use for this exercise
input_layer_size  = 400  # 20x20 Input Images of Digits
hidden_layer_size = 25   # 25 hidden units
num_labels = 10          # 10 labels, from 0 to 9

# Load the weights into variables Theta1 and Theta2
weights = loadmat(os.path.join('Data', 'ex4weights.mat'))

# Theta1 has size 25 x 401
# Theta2 has size 10 x 26
Theta1, Theta2 = weights['Theta1'], weights['Theta2']

# swap first and last columns of Theta2, due to legacy from MATLAB indexing, 
# since the weight file ex3weights.mat was saved based on MATLAB indexing
Theta2 = np.roll(Theta2, 1, axis=0)

# Unroll parameters 
nn_params = np.concatenate([Theta1.ravel(), Theta2.ravel()])

Функция прямой связи и стоимости

Теперь мы реализуем функцию стоимости и градиент для нейронной сети. Во-первых, функция nnCostFunction в следующей ячейке возвращает стоимость.

Напомним, что функция стоимости для нейронной сети (без регуляризации):

где h(theta)(Xi) вычисляется, как показано на рисунке нейронной сети выше, а K = 10 — это общее количество возможных меток. Обратите внимание, что h(theta)(Xi) k =3 – это активация (выходное значение) k-го блока вывода. Также напомню, что хотя исходные метки (в переменной y) были 0, 1, …, 9, для обучения нейронной сети нам нужно закодировать метки как векторы, содержащие только значения 0 или 1, так что

Например, если Xi — это изображение цифры 5, то соответствующий Yi (который следует использовать с функцией стоимости) должен быть 10-мерным вектором. с y[5] = 1, а остальные элементы равны 0.

Мы должны реализовать вычисление с прямой связью, которое вычисляет h(theta)(Xi) для каждого примера i и суммирует стоимость по всем примерам. Код также должен работать для набора данных любого размера с любым количеством меток.

def sigmoid(z):
    """
    Compute sigmoid function given the input z.
    
    Parameters
    ----------
    z : array_like
        The input to the sigmoid function. This can be a 1-D vector 
        or a 2-D matrix. 
    
    Returns
    -------
    g : array_like
        The computed sigmoid function. g has the same shape as z, since
        the sigmoid is computed element-wise on z.
        
    Instructions
    ------------
    Compute the sigmoid of each value of z (z can be a matrix, vector or scalar).
    """
    # convert input to a numpy array
    z = np.array(z)
    
    # You need to return the following variables correctly 
    g = np.zeros(z.shape)
    g=1/(1+np.exp(-z))

    return g
def nnCostFunction(nn_params,
                   input_layer_size,
                   hidden_layer_size,
                   num_labels,
                   X, y, lambda_=0.0):
    """
    Implements the neural network cost function and gradient for a two layer neural 
    network which performs classification. 
    
    Parameters
    ----------
    nn_params : array_like
        The parameters for the neural network which are "unrolled" into 
        a vector. This needs to be converted back into the weight matrices Theta1
        and Theta2.
    
    input_layer_size : int
        Number of features for the input layer. 
    
    hidden_layer_size : int
        Number of hidden units in the second layer.
    
    num_labels : int
        Total number of labels, or equivalently number of units in output layer. 
    
    X : array_like
        Input dataset. A matrix of shape (m x input_layer_size).
    
    y : array_like
        Dataset labels. A vector of shape (m,).
    
    lambda_ : float, optional
        Regularization parameter.
 
    Returns
    -------
    J : float
        The computed value for the cost function at the current weight values.
    
    grad : array_like
        An "unrolled" vector of the partial derivatives of the concatenatation of
        neural network weights Theta1 and Theta2.
    
    Instructions
    ------------
    You should complete the code by working through the following parts.
    
    - Part 1: Feedforward the neural network and return the cost in the 
              variable J. After implementing Part 1, you can verify that your
              cost function computation is correct by verifying the cost
              computed in the following cell.
    
    - Part 2: Implement the backpropagation algorithm to compute the gradients
              Theta1_grad and Theta2_grad. You should return the partial derivatives of
              the cost function with respect to Theta1 and Theta2 in Theta1_grad and
              Theta2_grad, respectively. After implementing Part 2, you can check
              that your implementation is correct by running checkNNGradients provided
              in the utils.py module.
    
              Note: The vector y passed into the function is a vector of labels
                    containing values from 0..K-1. You need to map this vector into a 
                    binary vector of 1's and 0's to be used with the neural network
                    cost function.
     
              Hint: We recommend implementing backpropagation using a for-loop
                    over the training examples if you are implementing it for the 
                    first time.
    
    - Part 3: Implement regularization with the cost function and gradients.
    
              Hint: You can implement this around the code for
                    backpropagation. That is, you can compute the gradients for
                    the regularization separately and then add them to Theta1_grad
                    and Theta2_grad from Part 2.
    
    Note 
    ----
    We have provided an implementation for the sigmoid function in the file 
    `utils.py` accompanying this assignment.
    """
    # Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
    # for our 2 layer neural network
    Theta1 = np.reshape(nn_params[:hidden_layer_size * (input_layer_size + 1)],
                        (hidden_layer_size, (input_layer_size + 1)))

    Theta2 = np.reshape(nn_params[(hidden_layer_size * (input_layer_size + 1)):],
                        (num_labels, (hidden_layer_size + 1)))

    # Encoding the labels to be instead of 0,1,2....9, [0,1,0,0,0,0,0,0,0,0]
    m = y.size
    y_new = np.zeros((m, num_labels))
    
    for i in range(m):
        
        y_new[i,y[i]] = 1
         
    # You need to return the following variables correctly 
    J = 0
    Theta1_grad = np.zeros(Theta1.shape)
    Theta2_grad = np.zeros(Theta2.shape)
        
#     Part 1: Feedforward the neural network and return the cost in the variable J

    X   = np.concatenate([np.ones((m, 1)), X], axis=1)
    a_1 = X
    
    a_2 = sigmoid(np.dot(a_1,Theta1.T))
    a_2 = np.concatenate([np.ones((a_2.shape[0], 1)), a_2], axis=1)
    
    ho  = sigmoid(np.dot(a_2,Theta2.T))
    J  =  (1/m)*(np.sum (np.sum( ((-y_new*np.log(ho))-((1-y_new)*np.log(1-ho))),axis=1)))
    return J, grad

как только вы закончите, вызовите nnCostFunction, используя загруженный набор параметров для Theta1 и Theta2. Вы должны увидеть, что стоимость составляет около 0,287629.

lambda_ = 0
J, _ = nnCostFunction(nn_params, input_layer_size, hidden_layer_size,
                   num_labels, X, y, lambda_)
print('Cost at parameters (loaded from ex4weights): %.6f ' % J)
print('The cost should be

Вывод

Cost at parameters (loaded from ex4weights): 0.287629 
The cost should be about                   : 0.287629.

Регулярная функция затрат

Функция стоимости для нейронных сетей с регуляризацией определяется следующим образом:

можно предположить, что нейронная сеть будет иметь только 3 слоя — входной слой, скрытый слой и выходной слой. Однако код должен работать для любого количества входных, скрытых и выходных единиц. Хотя мы явно перечислили индексы выше для Theta_1 и Theta_2 для ясности, обратите внимание, что код в целом должен работать с Theta_1 и Theta_2 любого размера. Обратите внимание, что вы не должны упорядочивать термины, соответствующие предвзятости. Для матриц Theta1 и Theta2 это соответствует первому столбцу каждой матрицы. Теперь вы должны добавить регуляризацию к вашей функции стоимости. Обратите внимание, что вы можете сначала вычислить функцию нерегулярных затрат J, используя существующую nnCostFunction, а затем добавить стоимость для условий регуляризации.

Вернемся к nnCostFunction для редактирования.

Как только вы закончите, следующая ячейка вызовет ваш nnCostFunction, используя загруженный набор параметров для Theta1 и Theta2, а лямбда = 1. Вы должны увидеть, что стоимость составляет около 0,383770.

def nnCostFunction(nn_params,
                   input_layer_size,
                   hidden_layer_size,
                   num_labels,
                   X, y, lambda_=0.0):
    """
    Implements the neural network cost function and gradient for a two layer neural 
    network which performs classification. 
    
    Parameters
    ----------
    nn_params : array_like
        The parameters for the neural network which are "unrolled" into 
        a vector. This needs to be converted back into the weight matrices Theta1
        and Theta2.
    
    input_layer_size : int
        Number of features for the input layer. 
    
    hidden_layer_size : int
        Number of hidden units in the second layer.
    
    num_labels : int
        Total number of labels, or equivalently number of units in output layer. 
    
    X : array_like
        Input dataset. A matrix of shape (m x input_layer_size).
    
    y : array_like
        Dataset labels. A vector of shape (m,).
    
    lambda_ : float, optional
        Regularization parameter.
 
    Returns
    -------
    J : float
        The computed value for the cost function at the current weight values.
    
    grad : array_like
        An "unrolled" vector of the partial derivatives of the concatenatation of
        neural network weights Theta1 and Theta2.
    
    Instructions
    ------------
    You should complete the code by working through the following parts.
    
    - Part 1: Feedforward the neural network and return the cost in the 
              variable J. After implementing Part 1, you can verify that your
              cost function computation is correct by verifying the cost
              computed in the following cell.
    
    - Part 2: Implement the backpropagation algorithm to compute the gradients
              Theta1_grad and Theta2_grad. You should return the partial derivatives of
              the cost function with respect to Theta1 and Theta2 in Theta1_grad and
              Theta2_grad, respectively. After implementing Part 2, you can check
              that your implementation is correct by running checkNNGradients provided
              in the utils.py module.
    
              Note: The vector y passed into the function is a vector of labels
                    containing values from 0..K-1. You need to map this vector into a 
                    binary vector of 1's and 0's to be used with the neural network
                    cost function.
     
              Hint: We recommend implementing backpropagation using a for-loop
                    over the training examples if you are implementing it for the 
                    first time.
    
    - Part 3: Implement regularization with the cost function and gradients.
    
              Hint: You can implement this around the code for
                    backpropagation. That is, you can compute the gradients for
                    the regularization separately and then add them to Theta1_grad
                    and Theta2_grad from Part 2.
    
    Note 
    ----
    We have provided an implementation for the sigmoid function in the file 
    `utils.py` accompanying this assignment.
    """
    # Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
    # for our 2 layer neural network
    Theta1 = np.reshape(nn_params[:hidden_layer_size * (input_layer_size + 1)],
                        (hidden_layer_size, (input_layer_size + 1)))

    Theta2 = np.reshape(nn_params[(hidden_layer_size * (input_layer_size + 1)):],
                        (num_labels, (hidden_layer_size + 1)))

    # Encoding the labels to be instead of 0,1,2....9, [0,1,0,0,0,0,0,0,0,0]
    m = y.size
    y_new = np.zeros((m, num_labels))
    
    for i in range(m):
        
        y_new[i,y[i]] = 1
         
    # You need to return the following variables correctly 
    J = 0
    Theta1_grad = np.zeros(Theta1.shape)
    Theta2_grad = np.zeros(Theta2.shape)
        
#     Part 1: Feedforward the neural network and return the cost in the variable J

    X   = np.concatenate([np.ones((m, 1)), X], axis=1)
    a_1 = X
    
    a_2 = sigmoid(np.dot(a_1,Theta1.T))
    a_2 = np.concatenate([np.ones((a_2.shape[0], 1)), a_2], axis=1)
    
    ho  = sigmoid(np.dot(a_2,Theta2.T))
    
    regularized_term = (lambda_/(2*m))*((np.sum(np.sum(Theta1[:,1:]**2,axis=1)))+(np.sum(np.sum(Theta2[:,1:]**2,axis=1))))
    
    J  =  (1/m)*(np.sum (np.sum( ((-y_new*np.log(ho))-((1-y_new)*np.log(1-ho))),axis=1)))+regularized_term
    return J, grad
# Weight regularization parameter (we set this to 1 here).
lambda_ = 1
J, _ = nnCostFunction(nn_params, input_layer_size, hidden_layer_size,
                      num_labels, X, y, lambda_)

print('Cost at parameters (loaded from ex4weights): %.6f' % J)
print('This value should be about                 : 0.383770.')

Вывод

Cost at parameters (loaded from ex4weights): 0.383770
This value should be about                 : 0.383770.

Если вы до сих пор следите за новостями, отличная работа

Обратное распространение

В этой части упражнения мы реализуем алгоритм обратного распространения ошибки для вычисления градиента функции стоимости нейронной сети. нам нужно будет обновить функцию nnCostFunction, чтобы она возвращала соответствующее значение для grad. Как только мы вычислим градиент, мы сможем обучить нейронную сеть, минимизируя функцию стоимости J (тета) с помощью продвинутого оптимизатора, такого как scipy optimize.minimize. сначала мы реализуем алгоритм обратного распространения для вычисления градиентов параметров для (нерегулярной) нейронной сети. После того, как мы убедились, что вычисление градиента для нерегуляризованного случая корректно, мы реализуем градиент для регуляризованной нейронной сети.

Сигмовидный градиент

Чтобы помочь вам приступить к этой части упражнения, мы сначала реализуем функцию сигмовидного градиента. Градиент для сигмовидной функции можно вычислить как:

def sigmoidGradient(z):
    """
    Computes the gradient of the sigmoid function evaluated at z. 
    This should work regardless if z is a matrix or a vector. 
    In particular, if z is a vector or matrix, you should return
    the gradient for each element.
    
    Parameters
    ----------
    z : array_like
        A vector or matrix as input to the sigmoid function. 
    
    Returns
    --------
    g : array_like
        Gradient of the sigmoid function. Has the same shape as z. 
    
    Instructions
    ------------
    Compute the gradient of the sigmoid function evaluated at
    each value of z (z can be a matrix, vector or scalar).
    
    Note
    ----
    We have provided an implementation of the sigmoid function 
    in `utils.py` file accompanying this assignment.
    """

    g = np.zeros(z.shape)

    # ====================== YOUR CODE HERE ======================
    g=(sigmoid(z)*(1-sigmoid(z)))


    # =============================================================
    return g

Когда мы закончим, следующая ячейка вызовет sigmoidGradient для заданного вектора z. Попробуйте проверить несколько значений, вызвав sigmoidGradient(z). Для больших значений (как положительных, так и отрицательных) z градиент должен быть близок к 0. Когда z = 0, градиент должен быть ровно 0,25. код также должен работать с векторами и матрицами. Для матрицы функция должна выполнять функцию сигмовидного градиента для каждого элемента.

z = np.array([-1, -0.5, 0, 0.5, 1])
g = sigmoidGradient(z)
print('Sigmoid gradient evaluated at [-1 -0.5 0 0.5 1]:\n  ')
print(g)

Вывод

Sigmoid gradient evaluated at [-1 -0.5 0 0.5 1]:
  
[0.19661193 0.23500371 0.25       0.23500371 0.19661193]

Случайная инициализация

При обучении нейронных сетей важно случайным образом инициализировать параметры для нарушения симметрии. Одной из эффективных стратегий случайной инициализации является случайный выбор значений для Theta_1 равномерно в диапазоне [-эпсилон, эпсилон]. Вы должны использовать эпсилон = 0,12$. Этот диапазон значений гарантирует, что параметры остаются небольшими, и делает обучение более эффективным.

def randInitializeWeights(L_in, L_out, epsilon_init=0.12):
    """
    Randomly initialize the weights of a layer in a neural network.
    
    Parameters
    ----------
    L_in : int
        Number of incomming connections.
    
    L_out : int
        Number of outgoing connections. 
    
    epsilon_init : float, optional
        Range of values which the weight can take from a uniform 
        distribution.
    
    Returns
    -------
    W : array_like
        The weight initialiatized to random values.  Note that W should
        be set to a matrix of size(L_out, 1 + L_in) as
        the first column of W handles the "bias" terms.
        
    Instructions
    ------------
    Initialize W randomly so that we break the symmetry while training
    the neural network. Note that the first column of W corresponds 
    to the parameters for the bias unit.
    """

    # You need to return the following variables correctly 
    W = np.zeros((L_out, 1 + L_in))

    # ====================== YOUR CODE HERE ======================
    W = np.random.rand(L_out, 1 + L_in) * 2 * epsilon_init - epsilon_init


    # ============================================================
    return W

Выполните следующую ячейку, чтобы инициализировать веса для двух слоев нейронной сети с помощью функции randInitializeWeights.

print('Initializing Neural Network Parameters ...')

initial_Theta1 = randInitializeWeights(input_layer_size, hidden_layer_size)
initial_Theta2 = randInitializeWeights(hidden_layer_size, num_labels)

# Unroll parameters
initial_nn_params = np.concatenate([initial_Theta1.ravel(), initial_Theta2.ravel()], axis=0)

Обратное распространение

Теперь вы реализуете алгоритм обратного распространения. Напомним, что интуиция, стоящая за алгоритмом обратного распространения ошибки, такова. Учитывая обучающий пример (x, y), мы сначала запустим «прямой проход», чтобы вычислить все активации по всей сети, включая выходное значение гипотезы h (тета). Затем для каждого узла j в слое L мы хотели бы вычислить «дельта члена ошибки [j]^[L], которая измеряет, насколько этот узел был «ответственным». ” для любых ошибок в нашем выводе.

Для выходного узла мы можем напрямую измерить разницу между активацией сети и истинным целевым значением и использовать это для определения delta_3. Для скрытых единиц вы вычислите delta_2 на основе средневзвешенного значения ошибок узлов в слое (l+1). Подробно, вот алгоритм обратного распространения (также изображен на рисунке выше). Вы должны реализовать шаги с 1 по 4 в цикле, который обрабатывает один пример за раз. Конкретно, вы должны реализовать цикл for for t in range(m) и поместить шаги 1-4 ниже внутри цикла for.

def nnCostFunction(nn_params,
                   input_layer_size,
                   hidden_layer_size,
                   num_labels,
                   X, y, lambda_=0.0):
    """
    Implements the neural network cost function and gradient for a two layer neural 
    network which performs classification. 
    
    Parameters
    ----------
    nn_params : array_like
        The parameters for the neural network which are "unrolled" into 
        a vector. This needs to be converted back into the weight matrices Theta1
        and Theta2.
    
    input_layer_size : int
        Number of features for the input layer. 
    
    hidden_layer_size : int
        Number of hidden units in the second layer.
    
    num_labels : int
        Total number of labels, or equivalently number of units in output layer. 
    
    X : array_like
        Input dataset. A matrix of shape (m x input_layer_size).
    
    y : array_like
        Dataset labels. A vector of shape (m,).
    
    lambda_ : float, optional
        Regularization parameter.
 
    Returns
    -------
    J : float
        The computed value for the cost function at the current weight values.
    
    grad : array_like
        An "unrolled" vector of the partial derivatives of the concatenatation of
        neural network weights Theta1 and Theta2.
    
    Instructions
    ------------
    You should complete the code by working through the following parts.
    
    - Part 1: Feedforward the neural network and return the cost in the 
              variable J. After implementing Part 1, you can verify that your
              cost function computation is correct by verifying the cost
              computed in the following cell.
    
    - Part 2: Implement the backpropagation algorithm to compute the gradients
              Theta1_grad and Theta2_grad. You should return the partial derivatives of
              the cost function with respect to Theta1 and Theta2 in Theta1_grad and
              Theta2_grad, respectively. After implementing Part 2, you can check
              that your implementation is correct by running checkNNGradients provided
              in the utils.py module.
    
              Note: The vector y passed into the function is a vector of labels
                    containing values from 0..K-1. You need to map this vector into a 
                    binary vector of 1's and 0's to be used with the neural network
                    cost function.
     
              Hint: We recommend implementing backpropagation using a for-loop
                    over the training examples if you are implementing it for the 
                    first time.
    
    - Part 3: Implement regularization with the cost function and gradients.
    
              Hint: You can implement this around the code for
                    backpropagation. That is, you can compute the gradients for
                    the regularization separately and then add them to Theta1_grad
                    and Theta2_grad from Part 2.
    
    Note 
    ----
    We have provided an implementation for the sigmoid function in the file 
    `utils.py` accompanying this assignment.
    """
    # Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
    # for our 2 layer neural network
    Theta1 = np.reshape(nn_params[:hidden_layer_size * (input_layer_size + 1)],
                        (hidden_layer_size, (input_layer_size + 1)))

    Theta2 = np.reshape(nn_params[(hidden_layer_size * (input_layer_size + 1)):],
                        (num_labels, (hidden_layer_size + 1)))

    # Encoding the labels to be instead of 0,1,2....9, [0,1,0,0,0,0,0,0,0,0]
    m = y.size
    y_new = np.zeros((m, num_labels))
    
    for i in range(m):
        
        y_new[i,y[i]] = 1
         
    # You need to return the following variables correctly 
    J = 0
    Theta1_grad = np.zeros(Theta1.shape)
    Theta2_grad = np.zeros(Theta2.shape)
        
#     Part 1: Feedforward the neural network and return the cost in the variable J

    X   = np.concatenate([np.ones((m, 1)), X], axis=1)
    a_1 = X
    
    a_2 = sigmoid(np.dot(a_1,Theta1.T))
    a_2 = np.concatenate([np.ones((a_2.shape[0], 1)), a_2], axis=1)
    
    ho  = sigmoid(np.dot(a_2,Theta2.T))
    
    regularized_term = (lambda_/(2*m))*((np.sum(np.sum(Theta1[:,1:]**2,axis=1)))+(np.sum(np.sum(Theta2[:,1:]**2,axis=1))))
    
    J  =  (1/m)*(np.sum (np.sum( ((-y_new*np.log(ho))-((1-y_new)*np.log(1-ho))),axis=1)))+regularized_term
    
#     - Part 2: Implement the backpropagation algorithm to compute the gradients

    for i in range(m):
        a1 = X[[i]]                                               # (1,401)
        z2 = np.dot(Theta1,a1.T)                                  # (25,401)(401,1) = (25,1)
        a2 = sigmoid(z2)                                          # (25,1)
        a2 = np.concatenate([np.ones([1,1]),a2])                  # (26,1)
        
        z3 = np.dot(Theta2,a2)                                    # (10,26)(26,1)=    (10,1)
        a3 = sigmoid(z3)                                          # (10,1)
        
        delta3 = a3-y_new[[i]].T                                  # (10,1)
        z2 = np.concatenate([np.ones([1,1]),z2])                  # (26,1)
        delta2 = np.dot(Theta2.T,delta3)*sigmoidGradient(z2)      # (26,10)(10,1) =   (26,1)      
        delta2 = delta2[1:,:]                                     # (25,1)
        
        Theta1_grad = Theta1_grad+np.dot(delta2,a1)               # ((25,1)(1,401)) = (25,401)
        Theta2_grad = Theta2_grad+np.dot(delta3,a2.T)             # (10,1)(1,26)=     (10,26)
        
#      Unroll gradients
    grad = np.concatenate([Theta1_grad.ravel(), Theta2_grad.ravel()])

    return J, grad

Регулярная нейронная сеть

После того, как вы успешно реализовали алгоритм обратного распространения, вы добавите регуляризацию в градиент. Оказывается, чтобы учесть регуляризацию, вы можете добавить это как дополнительный термин после вычисления градиентов с использованием обратного распространения ошибки.

Вы должны добавить регуляризацию, используя

def nnCostFunction(nn_params,
                   input_layer_size,
                   hidden_layer_size,
                   num_labels,
                   X, y, lambda_=0.0):
    """
    Implements the neural network cost function and gradient for a two layer neural 
    network which performs classification. 
    
    Parameters
    ----------
    nn_params : array_like
        The parameters for the neural network which are "unrolled" into 
        a vector. This needs to be converted back into the weight matrices Theta1
        and Theta2.
    
    input_layer_size : int
        Number of features for the input layer. 
    
    hidden_layer_size : int
        Number of hidden units in the second layer.
    
    num_labels : int
        Total number of labels, or equivalently number of units in output layer. 
    
    X : array_like
        Input dataset. A matrix of shape (m x input_layer_size).
    
    y : array_like
        Dataset labels. A vector of shape (m,).
    
    lambda_ : float, optional
        Regularization parameter.
 
    Returns
    -------
    J : float
        The computed value for the cost function at the current weight values.
    
    grad : array_like
        An "unrolled" vector of the partial derivatives of the concatenatation of
        neural network weights Theta1 and Theta2.
    
    Instructions
    ------------
    You should complete the code by working through the following parts.
    
    - Part 1: Feedforward the neural network and return the cost in the 
              variable J. After implementing Part 1, you can verify that your
              cost function computation is correct by verifying the cost
              computed in the following cell.
    
    - Part 2: Implement the backpropagation algorithm to compute the gradients
              Theta1_grad and Theta2_grad. You should return the partial derivatives of
              the cost function with respect to Theta1 and Theta2 in Theta1_grad and
              Theta2_grad, respectively. After implementing Part 2, you can check
              that your implementation is correct by running checkNNGradients provided
              in the utils.py module.
    
              Note: The vector y passed into the function is a vector of labels
                    containing values from 0..K-1. You need to map this vector into a 
                    binary vector of 1's and 0's to be used with the neural network
                    cost function.
     
              Hint: We recommend implementing backpropagation using a for-loop
                    over the training examples if you are implementing it for the 
                    first time.
    
    - Part 3: Implement regularization with the cost function and gradients.
    
              Hint: You can implement this around the code for
                    backpropagation. That is, you can compute the gradients for
                    the regularization separately and then add them to Theta1_grad
                    and Theta2_grad from Part 2.
    
    Note 
    ----
    We have provided an implementation for the sigmoid function in the file 
    `utils.py` accompanying this assignment.
    """
    # Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
    # for our 2 layer neural network
    Theta1 = np.reshape(nn_params[:hidden_layer_size * (input_layer_size + 1)],
                        (hidden_layer_size, (input_layer_size + 1)))

    Theta2 = np.reshape(nn_params[(hidden_layer_size * (input_layer_size + 1)):],
                        (num_labels, (hidden_layer_size + 1)))

    # Encoding the labels to be instead of 0,1,2....9, [0,1,0,0,0,0,0,0,0,0]
    m = y.size
    y_new = np.zeros((m, num_labels))
    
    for i in range(m):
        
        y_new[i,y[i]] = 1
         
    # You need to return the following variables correctly 
    J = 0
    Theta1_grad = np.zeros(Theta1.shape)
    Theta2_grad = np.zeros(Theta2.shape)
        
#     Part 1: Feedforward the neural network and return the cost in the variable J

    X   = np.concatenate([np.ones((m, 1)), X], axis=1)
    a_1 = X
    
    a_2 = sigmoid(np.dot(a_1,Theta1.T))
    a_2 = np.concatenate([np.ones((a_2.shape[0], 1)), a_2], axis=1)
    
    ho  = sigmoid(np.dot(a_2,Theta2.T))
    
    regularized_term = (lambda_/(2*m))*((np.sum(np.sum(Theta1[:,1:]**2,axis=1)))+(np.sum(np.sum(Theta2[:,1:]**2,axis=1))))
    
    J  =  (1/m)*(np.sum (np.sum( ((-y_new*np.log(ho))-((1-y_new)*np.log(1-ho))),axis=1)))+regularized_term
    
#     - Part 2: Implement the backpropagation algorithm to compute the gradients

    for i in range(m):
        a1 = X[[i]]                                               # (1,401)
        z2 = np.dot(Theta1,a1.T)                                  # (25,401)(401,1) = (25,1)
        a2 = sigmoid(z2)                                          # (25,1)
        a2 = np.concatenate([np.ones([1,1]),a2])                  # (26,1)
        
        z3 = np.dot(Theta2,a2)                                    # (10,26)(26,1)=    (10,1)
        a3 = sigmoid(z3)                                          # (10,1)
        
        delta3 = a3-y_new[[i]].T                                  # (10,1)
        z2 = np.concatenate([np.ones([1,1]),z2])                  # (26,1)
        delta2 = np.dot(Theta2.T,delta3)*sigmoidGradient(z2)      # (26,10)(10,1) =   (26,1)      
        delta2 = delta2[1:,:]                                     # (25,1)
        
        Theta1_grad = Theta1_grad+np.dot(delta2,a1)               # ((25,1)(1,401)) = (25,401)
        Theta2_grad = Theta2_grad+np.dot(delta3,a2.T)             # (10,1)(1,26)=     (10,26)
        
#    - Part 3: Implement regularization with the cost function and gradients.     
        
    Theta2_grad[:, 0]  = (1/m) * Theta2_grad[:, 0]
    Theta2_grad[:, 1:] = (1/m) * Theta2_grad[:, 1:]+((lambda_/m) * Theta2_grad[:, 1:])
    Theta1_grad[:, 0]  = (1/m) * Theta1_grad[:, 0]    # (10*26)
    Theta1_grad[:, 1:] = (1/m) * Theta1_grad[:, 1:]+((lambda_/m) * Theta1_grad[:, 1:])
    
#      Unroll gradients
    grad = np.concatenate([Theta1_grad.ravel(), Theta2_grad.ravel()])

    return J, grad

Обучение параметрам с использованием scipy.optimize.minimize

После того, как вы успешно реализовали функцию стоимости нейронной сети и вычисление градиента, на следующем шаге мы будем использовать минимизацию scipy, чтобы изучить хороший набор параметров.

#  After you have completed the assignment, change the maxiter to a larger
#  value to see how more training helps.
options= {'maxiter': 100}

#  You should also try different values of lambda
lambda_ = 1

# Create "short hand" for the cost function to be minimized
costFunction = lambda p: nnCostFunction(p, input_layer_size,
                                        hidden_layer_size,
                                        num_labels, X, y, lambda_)

# Now, costFunction is a function that takes in only one argument
# (the neural network parameters)
res = optimize.minimize(costFunction,
                        initial_nn_params,
                        jac=True,
                        method='TNC',
                        options=options)

# get the solution of the optimization
nn_params = res.x
        
# Obtain Theta1 and Theta2 back from nn_params
Theta1 = np.reshape(nn_params[:hidden_layer_size * (input_layer_size + 1)],
                    (hidden_layer_size, (input_layer_size + 1)))

Theta2 = np.reshape(nn_params[(hidden_layer_size * (input_layer_size + 1)):],
                    (num_labels, (hidden_layer_size + 1)))

После завершения обучения мы сообщим о точности обучения вашего классификатора, рассчитав процент правильных примеров. Если ваша реализация правильная, вы должны увидеть заявленную точность обучения около 95,3% (это может варьироваться примерно на 1% из-за случайной инициализации). Можно повысить точность обучения, обучив нейронную сеть большему количеству итераций. Мы рекомендуем вам попробовать обучить нейронную сеть большему количеству итераций (например, установить maxiter на 400), а также изменить параметр регуляризации $\lambda$. При правильных настройках обучения можно заставить нейронную сеть идеально соответствовать тренировочному набору.

pred = utils.predict(Theta1, Theta2, X)
print('Training Set Accuracy: %f' % (np.mean(pred == y) * 100))

Вывод

Training Set Accuracy: 96.380000

Если вы подписались на меня и написали код выше, поздравляю!

Вы построили нейронную сеть для мультиклассовой классификации с нуля. Это означает, что теперь вы можете использовать TensorFlow с полным пониманием гиперпараметров.

Увидимся в следующем блоге.

«Что хорошего в идее, если она так и останется идеей? Пытаться. Эксперимент. Повторить. Неудача. Попробуйте еще раз. Измени мир».

Саймон Синек