pythonarraysnumpysoftmax

Calculate the softmax of an array column-wise using numpy


Following https://classroom.udacity.com/courses/ud730/lessons/6370362152/concepts/63815621490923, I'm trying to write a "softmax" function which, when given a 2-dimensional array as input, calculates the softmax of each column. I wrote the following script to test it:

import numpy as np

#scores=np.array([1.0,2.0,3.0])

scores=np.array([[1,2,3,6],
                [2,4,5,6],
                [3,8,7,6]])

def softmax(x):
    if x.ndim==1:
        S=np.sum(np.exp(x))
        return np.exp(x)/S
    elif x.ndim==2:
        result=np.zeros_like(x)
        M,N=x.shape
        for n in range(N):
            S=np.sum(np.exp(x[:,n]))
            result[:,n]=np.exp(x[:,n])/S
        return result
    else:
        print("The input array is not 1- or 2-dimensional.")

s=softmax(scores)
print(s)

However, the result "s" turns out to be an array of zeros:

[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]

If I remove the "/S" in the for-loop, the 'un-normalized' result is as I would expect it to be; somehow the "/S" division appears to make all the elements zero instead dividing each element by S as I would expect it to. What is wrong with the code?


Solution

  • The reason for the "zeros" lies in the data type of the inputs, which are of the "int" type. Converting the input to "float" solved the problem:

    import numpy as np
    
    #scores=np.array([1.0,2.0,3.0])
    
    scores=np.array([[1,2,3,6],
                    [2,4,5,6],
                    [3,8,7,6]])
    
    def softmax(x):
        x=x.astype(float)
        if x.ndim==1:
            S=np.sum(np.exp(x))
            return np.exp(x)/S
        elif x.ndim==2:
            result=np.zeros_like(x)
            M,N=x.shape
            for n in range(N):
                S=np.sum(np.exp(x[:,n]))
                result[:,n]=np.exp(x[:,n])/S
            return result
        else:
            print("The input array is not 1- or 2-dimensional.")
    
    s=softmax(scores)
    print(s)
    

    Note that I've added "x=x.astype(float)" to the first line of the function definition. This yields the expected output:

    [[ 0.09003057  0.00242826  0.01587624  0.33333333]
     [ 0.24472847  0.01794253  0.11731043  0.33333333]
     [ 0.66524096  0.97962921  0.86681333  0.33333333]]