blob: 84627a9b13d50edcd5d53f9d8dce3885eaf9f7dd [file] [log] [blame]
#-------------------------------------------------------------
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#-------------------------------------------------------------
/*
* Softmax classifier layer.
*/
forward = function(matrix[double] scores)
return (matrix[double] probs) {
/*
* Computes the forward pass for a softmax classifier. The input
* has N examples, each with D values that are interpreted as
* unnormalized, log-probabilities for each of D classes. The softmax
* function transforms these values to normalized probabilities across
* the D classes, for every example.
*
* This can be interpreted as a generalization of the sigmoid
* function to multiple classes.
*
* `probs_ij = e^scores_ij / sum(e^scores_i)`
*
* Inputs:
* - scores: Inputs, of shape (N, D).
*
* Outputs:
* - probs: Outputs, of shape (N, D).
*/
# For numerical stability, we subtract the max score of an example from all scores for that
# example. This is equivalent to the original formulation:
# e^scores_i / sum(e^scores_i) == C*e^scores_i / C*sum(e^scores_i)
# == e^(scores_i+log(C)) / sum(e^(scores_i+log(C))
# set log(C) = -max(scores_i):
# == e^(scores_i-max(scores_i)) / sum(e^(scores_i-max(scores_i))
scores = scores - rowMaxs(scores) # numerical stability
unnorm_probs = exp(scores) # unnormalized probabilities
probs = unnorm_probs / rowSums(unnorm_probs) # normalized probabilities
}
backward = function(matrix[double] dprobs, matrix[double] scores)
return (matrix[double] dscores) {
/*
* Computes the backward pass for a softmax classifier.
*
* Note that dscores_ij has multiple source branches:
*
* ```
* dprobs_ij/dscores_ij = probs_ij * (1 - probs_ij)
* dprobs_ik/dscores_ij = -probs_ik * probs_ij, for all k != j
*
* dloss/dscores_ij =
* (dloss/dprobs_ij * dprobs_ij/dscores_ij)
* + sum_{k!=j}(dloss/dprobs_ik * dprobs_ik/dscores_ij)
* ```
*
* Inputs:
* - dprobs: Gradient wrt `probs` from upstream, of shape (N, D).
* - scores: Inputs, of shape (N, D).
*
* Outputs:
* - dscores: Gradient wrt `scores`, of shape (N, D).
*/
scores = scores - rowMaxs(scores) # numerical stability
unnorm_probs = exp(scores) # unnormalized probabilities
probs = unnorm_probs / rowSums(unnorm_probs) # normalized probabilities
# After some cancellation:
# dscores = dprobs*probs - probs*rowSums(dprobs*probs)
dtemp = dprobs * probs
dscores = dtemp - probs*rowSums(dtemp)
}