Регуляризация отсева

машинное обучение глубокое обучение алгоритм

Регуляризация отсева

Чтобы предотвратить проблему переобучения, наиболее часто используемым методом является регуляризация L2, то есть добавление члена регуляризации L2 после функции стоимости. Про регуляризацию L2 я много раз рассказывал в предыдущем блоге, поэтому повторяться здесь не буду. В этом блоге в основном представлена ​​регуляризация отсева (регуляризация отсева), предложенная Шриваставой в 2014 году:Dropout: A Simple Way to Prevent Neural Networks from Overtting. Идея Dropout на самом деле очень проста и груба: для каждого слоя сети случайным образом отбрасываются какие-то единицы. Как показано ниже (изображение из отсева Шриваставы):

dropout

Относительно того, почему отсев может облегчить переоснащение, ng дает еще два интуитивно понятных объяснения:

  • Именно потому, что некоторые единицы случайным образом отбрасываются в каждом слое, обученная сеть намного меньше, чем обычная сеть, что в определенной степени объясняет проблему предотвращения переобучения.
  • Простая однослойная сеть, как показано на рисунке ниже, поскольку каждый признак может быть отброшен, вся сеть не будет смещена в сторону определенного признака (весу определенного признака присваивается большое значение), и каждый признак будет Веса назначаются очень маленькими, что несколько похоже на регуляризацию L2, которая может уменьшить переоснащение.
    单层网络

Этот метод может показаться сумасшедшим, но он работает. Давайте начнем рассматривать отсев регулярного члена с точки зрения технической реализации, наиболее важным параметром здесь являетсяkeep\_prob, называется зарезервированная вероятность (опять же,1 - keep\_prob– вероятность падения), напримерkeep\_prob = 0.8, это означает, что слой случайным образом сохраняет 80 % нейронных единиц (то есть 20 % единиц отбрасываются). Обычно отсев реализован Техника регуляризации называется Инвертированное отсев, предполагая, что для третьего слоя конкретная реализация инвертированного отсева:

1.  d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep_prob
2.  a3 = np.multiply(a3,d3)
3.  a3 = a3 / keep_prob
4.  z4 = np.dot(w4,a3) + b4

Несколько замечаний по инструкциям для описанных выше шагов:

Шаг 1, создайте новый размер иa3Та же матрица вероятностей, по сути, тот же код, потому что каждый раз это случайное число, его можно сохранить только приблизительноkeep\_probтак много, как,keep\_prob = 0.8Возможно, d3 составляет всего 79% от 1, что означает, что сохраняется только 79% единиц, но чем больше количество единиц скрытого слоя, тем ближе оно к 80%.
Шаг 2,a3иd3Точечное умножение, т.е. сохранениеkeep\_probединицы, остальное1- keep\_probблок деактивирован.
Шаг 3, потому что есть1- keep\_probустройство отключено, поэтомуa3ожидания снижаются1- keep\_prob, поэтому мы используемa3 / keep\_prob,такa3ожидания остаются неизменными. Это перевернутый дропаут.

Вот для чего предназначен перевернутый дропаут. Следующие две динамические диаграммы используются для демонстрации процесса отсева (материал взят из класса глубокого обучения ng):

dropout
dropout1

Далее мы в основном сосредоточимся на реализации кода. Предположим, что наша функция активации сети использует relu, а выходной слой использует сигмоид. Нам нужно использовать только переднийПошаговый почерк нейронной сетиНа основе прямого распространения (forward propagation) и обратного распространения (backward propagation) немного изменены:

Прямое распространение

def forward_propagation_with_dropout(X, parameters, keep_prob = 0.8):
    """
    X -- input dataset, of shape (input size, number of examples)
    parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2",...,"WL", "bL"
                    W -- weight matrix of shape (size of current layer, size of previous layer)
                    b -- bias vector of shape (size of current layer,1)
    keep_prob: probability of keeping a neuron active during drop-out, scalar
    :return:
    AL: the output of the last Layer(y_predict)
    caches: list, every element is a tuple:(W,b,z,A_pre)
    """
    np.random.seed(1)  #random seed
    L = len(parameters) // 2  #number of layer
    A = X
    caches = [(None,None,None,X,None)]  #用于存储每一层的,w,b,z,A,D第0层w,b,z用none代替
    # calculate from 1 to L-1 layer
    for l in range(1, L):
        A_pre = A
        W = parameters["W" + str(l)]
        b = parameters["b" + str(l)]
        z = np.dot(W, A_pre) + b  # 计算z = wx + b
        A = relu(z)  # relu activation function
        D = np.random.rand(A.shape[0], A.shape[1]) #initialize matrix D
        D = (D < keep_prob) #convert entries of D to 0 or 1 (using keep_prob as the threshold)
        A = np.multiply(A, D) #shut down some neurons of A
        A = A / keep_prob #scale the value of neurons that haven't been shut down
        caches.append((W, b, z, A,D))
    # calculate Lth layer
    WL = parameters["W" + str(L)]
    bL = parameters["b" + str(L)]
    zL = np.dot(WL, A) + bL
    AL = sigmoid(zL)
    caches.append((WL, bL, zL, A))
    return AL, caches

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

def backward_propagation_with_dropout(AL, Y, caches, keep_prob = 0.8):
    """
        Implement the backward propagation presented in figure 2.
        Arguments:
        X -- input dataset, of shape (input size, number of examples)
        Y -- true "label" vector (containing 0 if cat, 1 if non-cat)
        caches -- caches output from forward_propagation(),(W,b,z,pre_A)
        keep_prob: probability of keeping a neuron active during drop-out, scalar
        Returns:
        gradients -- A dictionary with the gradients with respect to dW,db
        """
    m = Y.shape[1]
    L = len(caches) - 1
    # print("L:   " + str(L))
    # calculate the Lth layer gradients
    prev_AL = caches[L - 1][3]
    dzL = 1. / m * (AL - Y)
    dWL = np.dot(dzL, prev_AL.T)
    dbL = np.sum(dzL, axis=1, keepdims=True)
    gradients = {"dW" + str(L): dWL, "db" + str(L): dbL}
    # calculate from L-1 to 1 layer gradients
    for l in reversed(range(1, L)): # L-1,L-2,...,1
        post_W = caches[l + 1][0]  # 要用后一层的W
        dz = dzL  # 用后一层的dz
        dal = np.dot(post_W.T, dz)
        Dl = caches[l][4] #当前层的D
        dal = np.multiply(dal, Dl) #Apply mask Dl to shut down the same neurons as during the forward propagation
        dal = dal / keep_prob #Scale the value of neurons that haven't been shut down
        Al = caches[l][3]  #当前层的A
        dzl = np.multiply(dal, relu_backward(Al))#也可以用dzl=np.multiply(dal, np.int64(Al > 0))来实现
        prev_A = caches[l-1][3]  # 前一层的A
        dWl = np.dot(dzl, prev_A.T)
        dbl = np.sum(dzl, axis=1, keepdims=True)

        gradients["dW" + str(l)] = dWl
        gradients["db" + str(l)] = dbl
        dzL = dzl  # 更新dz
    return gradients

Есть несколько вещей, которые следует отметить в отношении отсева:

  • Используйте отсев только при обучении сети, не используйте отсев на тестовом наборе (при прогнозировании), что означает, что когда мы используем обученные параметры для прямого распространения при прогнозировании (классификации), нам нужно использовать отсев!
  • отсев - это метод регуляризации

Давайте проведем простое сравнение в наборе данных load_breast_cancer в sklean, сети без регулярного термина, сети с обычным термином L2 и сетевом эффекте с отсевом:

Сеть без регулярного члена, точность: 0,912

dnn

Сеть регулярного члена L2, показатель точности: 0,921

dnn_l2

Сеть выпадающих регулярных терминов, показатель точности: 0,929

dnn_dropout



Полный код выложен на github:
Сеть с обычным термином L2:deep_neural_network_with_L2
Сеть с отсевом обычного срока:deep_neural_network_with_dropout