def softmax_with_cross_entropy(preds, target_index): """ Computes softmax and cross-entropy loss for model predictions, including the gradient Arguments: preds: np array, shape is either (N) or (batch_size, N) - classifier output target_index: np array of int, shape is (1) or (batch_size) - index of the true class for given sample(s) Returns: loss, single value - cross-entropy loss d_preds, np array same shape as predictions - gradient of predictions by loss value """ # TODO_: Copy from the previous assignment # raise Exception("Not implemented!") preds = preds.copy() probs = softmax(preds) loss = cross_entropy_loss(probs, target_index).mean() mask = np.zeros_like(preds) mask[np.arange(len(mask)), target_index] = 1 # mask[target_index] = 1 d_preds = - (mask - softmax(preds)) / mask.shape[0] return loss, d_preds
# В общем виде cross-entropy определена следующим образом: # # $$ # H(p,q) = -\displaystyle\sum_x p(x)\,\log q(x). # $$ # # где $x$ - все классы, $p(x)$ - истинная вероятность принадлежности сэмпла классу $x$, а $q(x)$ - вероятность принадлежности классу $x$, предсказанная моделью. # В нашем случае сэмпл принадлежит только одному классу, индекс которого передается функции. Для него $p(x)$ равна 1, а для остальных классов - 0. # # Это позволяет реализовать функцию проще! #%% from linear_classifer import cross_entropy_loss probs = softmax(np.array([[-5, 0, 5]], )) cross_entropy_loss(probs, np.array([1])) #%% [markdown] # После того как мы реализовали сами функции, мы можем реализовать градиент. # # Оказывается, что вычисление градиента становится гораздо проще, если объединить эти функции в одну, которая сначала вычисляет вероятности через softmax, а потом использует их для вычисления функции ошибки через cross-entropy loss. # # Эта функция `softmax_with_cross_entropy` будет возвращает и значение ошибки, и градиент по входным параметрам. Мы проверим корректность реализации с помощью `check_gradient`. #%% from linear_classifer import softmax_with_cross_entropy loss, grad = softmax_with_cross_entropy(np.array([[1, 0, 0]]), np.array([1])) print(loss, grad) check_gradient(lambda x: softmax_with_cross_entropy(x, np.array([1])),
def array_sum(x): assert x.shape == (2, ), x.shape return np.sum(x), np.ones_like(x) check_gradient(array_sum, np.array([3.0, 2.0])) def array_2d_sum(x): assert x.shape == (2, 2) return np.sum(x), np.ones_like(x) check_gradient(array_2d_sum, np.array([[3.0, 2.0], [1.0, 0.0]])) # TODO Implement softmax and cross-entropy for single sample probs = linear_classifer.softmax(np.array([-10, 0, 10])) # Make sure it works for big numbers too! probs = linear_classifer.softmax(np.array([1000, 0, 0])) assert np.isclose(probs[0], 1.0) # My test batch softmax probs = linear_classifer.softmax( np.array([[-10, 0, 10], [30, 4, 5], [2, 6, 8]])) probs = linear_classifer.softmax(np.array([-5, 0, 5])) linear_classifer.cross_entropy_loss(probs, 1)