Jak zaimplementować funkcję Softmax w Pythonie

247

Z klasy głębokiego uczenia Udacity softmax y_i jest po prostu wykładniczym podzielonym przez sumę wykładniczego całego wektora Y:

wprowadź opis zdjęcia tutaj

Gdzie S(y_i)jest funkcja softmax y_ii ejest wykładnicza i jjest nie. kolumn w wektorze wejściowym Y.

Próbowałem następujące:

import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

scores = [3.0, 1.0, 0.2]
print(softmax(scores))

który zwraca:

[ 0.8360188   0.11314284  0.05083836]

Ale sugerowanym rozwiązaniem było:

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    return np.exp(x) / np.sum(np.exp(x), axis=0)

co daje takie same wyniki jak pierwsza implementacja , mimo że pierwsza implementacja wyraźnie bierze różnicę między każdą kolumną a wartością maksymalną, a następnie dzieli przez sumę.

Czy ktoś może matematycznie pokazać, dlaczego? Czy jedno jest prawidłowe, a drugie złe?

Czy implementacja jest podobna pod względem złożoności kodu i czasu? Który jest bardziej wydajny?

alvas
źródło
6
Ciekawe, dlaczego próbowałeś zaimplementować go w ten sposób za pomocą funkcji max. Co sprawiło, że pomyślałeś o tym w ten sposób?
BBischof
1
Nie wiem, pomyślałem, że potraktowanie maksimum jako 0 i coś w rodzaju przeniesienia wykresu w lewo, a klip przy 0 pomaga. Potem mój zasięg jakby skrócił się od -inf to +infdo -inf to 0. Chyba przesadziłem. hahahaaa
alvas
1
Nadal mam jedno pod) pytanie, na które nie wydaje się odpowiedzi poniżej. Jakie znaczenie ma axis = 0sugerowana odpowiedź Udacity?
Parva Thakkar
3
jeśli spojrzysz na dokumentację numpy, omawia ona, co robi suma (x, oś = 0) - i podobnie oś = 1--. W skrócie, zapewnia kierunek sumowania tablicy tablic. W takim przypadku nakazuje sumowanie wzdłuż wektorów. W tym przypadku odpowiada to mianownikom w funkcji softmax.
BBischof
3
Jak co drugi tydzień, istnieje poprawniejsza odpowiedź do momentu, gdy moja matematyka nie jest wystarczająco dobra, aby zdecydować, kto jest poprawny =) Jakikolwiek matematyk, który nie udzielił odpowiedzi, może pomóc zdecydować, która jest poprawna?
alvas

Odpowiedzi:

137

Oba są poprawne, ale twoje jest preferowane z punktu widzenia stabilności numerycznej.

Zaczynasz od

e ^ (x - max(x)) / sum(e^(x - max(x))

Wykorzystując fakt, że a ^ (b - c) = (a ^ b) / (a ​​^ c) mamy

= e ^ x / (e ^ max(x) * sum(e ^ x / e ^ max(x)))

= e ^ x / sum(e ^ x)

Tak mówi druga odpowiedź. Możesz zamienić max (x) na dowolną zmienną i to by się anulowało.

Trevor Merrifield
źródło
4
Ponowne sformatowanie odpowiedzi @ TrevorM w celu dalszego wyjaśnienia: e ^ (x - max (x)) / suma (e ^ (x - max (x)) przy użyciu ^ (b - c) = (a ^ b) / (a ​​^ c) mamy, = e ^ x / {e ^ max (x) * suma (e ^ x / e ^ max (x))} = e ^ x / suma (e ^ x)
shanky_thebearer
5
@ Trevor Merrifield, nie sądzę, aby pierwsze podejście miało jakikolwiek „niepotrzebny termin”. W rzeczywistości jest to lepsze niż drugie podejście. Dodałem ten punkt jako osobną odpowiedź.
Shagun Sodhani
6
@Shagun Masz rację. Te dwa są matematycznie równoważne, ale nie rozważałem stabilności numerycznej.
Trevor Merrifield
Mam nadzieję, że nie masz nic przeciwko: zredagowałem „niepotrzebny termin” na wypadek, gdyby ludzie nie czytali komentarzy (lub komentarze znikały). Ta strona generuje spory ruch z wyszukiwarek i jest to obecnie pierwsza odpowiedź, jaką widzą ludzie.
Alex Riley,
Zastanawiam się, dlaczego odejmujesz max (x), a nie max (abs (x)) (napraw znak po ustaleniu wartości). Jeśli wszystkie twoje wartości są poniżej zera i są bardzo duże w wartości bezwzględnej, a tylko wartość (maksymalna) jest bliska zeru, odjęcie maksimum niczego nie zmieni. Czy nadal nie byłby niestabilny numerycznie?
Cerno
102

(Cóż ... tutaj wiele zamieszania, zarówno w pytaniu, jak iw odpowiedziach ...)

Na początek dwa rozwiązania (tj. Twoje i sugerowane) nie są równoważne; okazuje się, że są równoważne tylko w szczególnym przypadku tablic wyników 1-D. Odkryłbyś to, gdybyś wypróbował również tablicę wyników 2-D w podanym przykładzie quizu Udacity.

Pod względem wyników jedyną faktyczną różnicą między tymi dwoma rozwiązaniami jest axis=0argument. Aby zobaczyć, że tak jest, spróbujmy rozwiązania ( your_softmax) i jedyną różnicą jest axisargument:

import numpy as np

# your solution:
def your_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

# correct solution:
def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0) # only difference

Jak powiedziałem, dla tablicy wyników 1-D wyniki są rzeczywiście identyczne:

scores = [3.0, 1.0, 0.2]
print(your_softmax(scores))
# [ 0.8360188   0.11314284  0.05083836]
print(softmax(scores))
# [ 0.8360188   0.11314284  0.05083836]
your_softmax(scores) == softmax(scores)
# array([ True,  True,  True], dtype=bool)

Niemniej jednak oto wyniki dla tablicy wyników 2-D podane w quizie Udacity jako przykład testu:

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

print(your_softmax(scores2D))
# [[  4.89907947e-04   1.33170787e-03   3.61995731e-03   7.27087861e-02]
#  [  1.33170787e-03   9.84006416e-03   2.67480676e-02   7.27087861e-02]
#  [  3.61995731e-03   5.37249300e-01   1.97642972e-01   7.27087861e-02]]

print(softmax(scores2D))
# [[ 0.09003057  0.00242826  0.01587624  0.33333333]
#  [ 0.24472847  0.01794253  0.11731043  0.33333333]
#  [ 0.66524096  0.97962921  0.86681333  0.33333333]]

Wyniki są różne - druga jest rzeczywiście identyczna z oczekiwaną w quizie Udacity, gdzie wszystkie kolumny rzeczywiście sumują się do 1, co nie jest w przypadku pierwszego (błędnego) wyniku.

Tak więc całe zamieszanie dotyczyło szczegółów implementacyjnych - axisargumentu. Zgodnie z dokumentacją numpy.sum :

Domyślnie, oś = Brak, sumuje wszystkie elementy tablicy wejściowej

podczas gdy tutaj chcemy podsumować wierszowo, stąd axis=0. W przypadku tablicy 1-D suma (tylko) wiersza i suma wszystkich elementów są identyczne, stąd twoje identyczne wyniki w tym przypadku ...

axisProblem na bok, implementacja (czyli wybór odjąć max pierwszy) jest rzeczywiście lepsze niż sugerowane rozwiązanie! W rzeczywistości jest to zalecany sposób implementacji funkcji softmax - patrz tutaj uzasadnienie (stabilność liczbowa, na co wskazują również inne odpowiedzi tutaj).

Desertnaut
źródło
Cóż, jeśli chodzi tylko o tablicę wielowymiarową. Pierwsze rozwiązanie można łatwo naprawić, dodając axisargument do obu maxi sum. Jednak pierwsza implementacja jest jeszcze lepsza, ponieważ można łatwo przepełnić podczas przyjmowaniaexp
Louis Yang
@LouisYang Nie obserwuję; które jest „pierwszym” rozwiązaniem? Który nie używa exp? Co jeszcze tu zmodyfikowano poza dodaniem axisargumentu?
desertnaut
Pierwsze rozwiązanie odnosi się do rozwiązania z @alvas. Różnica polega na tym, że w sugerowanym rozwiązaniu w pytaniu o alvasów brakuje części odejmowania maksimum. Może to łatwo spowodować przepełnienie, na przykład exp (1000) / (exp (1000) + exp (1001)) vs exp (-1) / (exp (-1) + exp (0)) są takie same w matematyce, ale pierwszy przepełni się.
Louis Yang,
@LouisYang nadal, nie jestem pewien, czy rozumiem konieczność twojego komentarza - wszystko to zostało już wyraźnie wyjaśnione w odpowiedzi.
desertnaut
@LouisYang, proszę, nie daj się zwieść (późniejszej) popularności wątku i spróbuj wyobrazić sobie kontekst, w którym zaproponowano własną odpowiedź: zdziwiony OP („ oba dają ten sam wynik ”) i (wciąż!) Zaakceptowana odpowiedź twierdząc, że „ obie są poprawne ” (dobrze, są one nie ). Odpowiedź nigdy nie miała brzmieć „ jest to najbardziej poprawny i najskuteczniejszy sposób na obliczenie softmax w ogóle ”; Oznaczało to po prostu, aby uzasadnić , dlaczego w konkretnych witrynie Udacity quizu omawianym roztwory 2 są nie równoważne.
desertnaut
56

To naprawdę komentarz do odpowiedzi desertnaut, ale nie mogę tego jeszcze komentować z powodu mojej reputacji. Jak zauważył, twoja wersja jest poprawna tylko wtedy, gdy twój wkład składa się z pojedynczej próbki. Jeśli dane wejściowe składają się z kilku próbek, są błędne. Jednak rozwiązanie desertnaut jest również błędne. Problem polega na tym, że raz przyjmuje dane jednowymiarowe, a następnie przyjmuje dane dwuwymiarowe. Pozwól, że ci to pokażę.

import numpy as np

# your solution:
def your_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

# desertnaut solution (copied from his answer): 
def desertnaut_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0) # only difference

# my (correct) solution:
def softmax(z):
    assert len(z.shape) == 2
    s = np.max(z, axis=1)
    s = s[:, np.newaxis] # necessary step to do broadcasting
    e_x = np.exp(z - s)
    div = np.sum(e_x, axis=1)
    div = div[:, np.newaxis] # dito
    return e_x / div

Weźmy przykład pustynnych:

x1 = np.array([[1, 2, 3, 6]]) # notice that we put the data into 2 dimensions(!)

To jest wynik:

your_softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

desertnaut_softmax(x1)
array([[ 1.,  1.,  1.,  1.]])

softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

Widać, że wersja desernauts zawiodłaby w tej sytuacji. (Nie byłoby tak, gdyby dane wejściowe były tylko jednowymiarowe jak np. Tablica ([1, 2, 3, 6]).

Użyjmy teraz 3 próbek, ponieważ z tego powodu używamy dwuwymiarowego wejścia. Poniższy x2 nie jest taki sam jak ten z przykładu dezerterów.

x2 = np.array([[1, 2, 3, 6],  # sample 1
               [2, 4, 5, 6],  # sample 2
               [1, 2, 3, 6]]) # sample 1 again(!)

Dane wejściowe składają się z partii z 3 próbkami. Ale próbka pierwsza i trzecia są w zasadzie takie same. Oczekujemy teraz 3 wierszy aktywacji softmax, przy czym pierwsza powinna być taka sama jak trzecia, a także taka sama jak nasza aktywacja x1!

your_softmax(x2)
array([[ 0.00183535,  0.00498899,  0.01356148,  0.27238963],
       [ 0.00498899,  0.03686393,  0.10020655,  0.27238963],
       [ 0.00183535,  0.00498899,  0.01356148,  0.27238963]])


desertnaut_softmax(x2)
array([[ 0.21194156,  0.10650698,  0.10650698,  0.33333333],
       [ 0.57611688,  0.78698604,  0.78698604,  0.33333333],
       [ 0.21194156,  0.10650698,  0.10650698,  0.33333333]])

softmax(x2)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047],
       [ 0.01203764,  0.08894682,  0.24178252,  0.65723302],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

Mam nadzieję, że widać, że tak jest tylko w przypadku mojego rozwiązania.

softmax(x1) == softmax(x2)[0]
array([[ True,  True,  True,  True]], dtype=bool)

softmax(x1) == softmax(x2)[2]
array([[ True,  True,  True,  True]], dtype=bool)

Dodatkowo, oto wyniki implementacji softmax TensorFlows:

import tensorflow as tf
import numpy as np
batch = np.asarray([[1,2,3,6],[2,4,5,6],[1,2,3,6]])
x = tf.placeholder(tf.float32, shape=[None, 4])
y = tf.nn.softmax(x)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(y, feed_dict={x: batch})

A wynik:

array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037045],
       [ 0.01203764,  0.08894681,  0.24178252,  0.657233  ],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037045]], dtype=float32)
ChuckFive
źródło
6
To byłby cholernie komentarz ;-)
Michael Benjamin
27
np.exp (z) / np.sum (np.exp (z), axis = 1, keepdims = True) osiąga ten sam wynik, co funkcja softmax. kroki zs są niepotrzebne.
PabTorre
W miejsce` s = s[:, np.newaxis], s = s.reshape(z.shape[0],1)powinien również działać.
Debashish,
2
tyle niepoprawnych / nieefektywnych rozwiązań na tej stronie. Zrób sobie przysługę i skorzystaj z PabTorre's
Miss Palmer
@PabTorre miałeś na myśli oś = -1? oś = 1 nie działa dla danych jednowymiarowych
DiehardTheTryhard
36

Powiedziałbym, że chociaż oba są poprawne matematycznie, pod względem implementacji, pierwszy jest lepszy. Podczas obliczania softmax wartości pośrednie mogą stać się bardzo duże. Dzielenie dwóch dużych liczb może być niestabilne numerycznie. Te notatki (ze Stanford) wspominają sztuczkę normalizacyjną, która jest zasadniczo tym, co robisz.

Shagun Sodhani
źródło
3
Skutków katastroficznego anulowania nie można nie docenić.
Cesar
24

sklearn oferuje również implementację softmax

from sklearn.utils.extmath import softmax
import numpy as np

x = np.array([[ 0.50839931,  0.49767588,  0.51260159]])
softmax(x)

# output
array([[ 0.3340521 ,  0.33048906,  0.33545884]]) 
Roman Orac
źródło
3
Jak dokładnie odpowiada to konkretne pytanie, które dotyczy samej implementacji , a nie dostępności w bibliotece innej firmy?
desertnaut
8
Szukałem implementacji strony trzeciej, aby zweryfikować wyniki obu podejść. W ten sposób pomaga ten komentarz.
Eugenio F. Martinez Pacheco
13

Z matematycznego punktu widzenia obie strony są równe.

I możesz to łatwo udowodnić. Let's m=max(x). Teraz twoja funkcja softmaxzwraca wektor, którego i-ta współrzędna jest równa

wprowadź opis zdjęcia tutaj

zauważ, że działa to na dowolne m, ponieważ na wszystkie (nawet złożone) liczbye^m != 0

  • z punktu widzenia złożoności obliczeniowej są one również równoważne i oba działają O(n) czasie, gdzie njest rozmiar wektora.

  • z punktu widzenia stabilności numerycznej preferowane jest pierwsze rozwiązanie, ponieważ e^xrośnie ono bardzo szybko, a nawet przy dość niewielkich wartościach xprzepełnia się. Odejmowanie maksymalnej wartości pozwala pozbyć się tego przelewu. Aby praktycznie doświadczyć rzeczy, o których mówiłem, spróbuj włączyć x = np.array([1000, 5])obie funkcje. Jeden zwróci prawidłowe prawdopodobieństwo, drugi przepełni sięnan

  • twoje rozwiązanie działa tylko dla wektorów (quiz Udacity chce, abyś również obliczył dla macierzy). Aby to naprawić, musisz użyćsum(axis=0)

Salvador Dali
źródło
1
Kiedy warto obliczyć softmax na macierzy, a nie na wektorze? tj. jakie modele macierzy wyjściowej? Czy może być jeszcze bardziej wymiarowy?
mrgloom
2
masz na myśli pierwsze rozwiązanie w „z punktu widzenia stabilności numerycznej preferowane jest drugie rozwiązanie ...”?
Dataman
10

EDYTOWAĆ . Począwszy od wersji 1.2.0, scipy zawiera softmax jako specjalną funkcję:

https://scipy.github.io/devdocs/generated/scipy.special.softmax.html

Napisałem funkcję nakładającą softmax na dowolną oś:

def softmax(X, theta = 1.0, axis = None):
    """
    Compute the softmax of each element along an axis of X.

    Parameters
    ----------
    X: ND-Array. Probably should be floats. 
    theta (optional): float parameter, used as a multiplier
        prior to exponentiation. Default = 1.0
    axis (optional): axis to compute values along. Default is the 
        first non-singleton axis.

    Returns an array the same size as X. The result will sum to 1
    along the specified axis.
    """

    # make X at least 2d
    y = np.atleast_2d(X)

    # find axis
    if axis is None:
        axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)

    # multiply y against the theta parameter, 
    y = y * float(theta)

    # subtract the max for numerical stability
    y = y - np.expand_dims(np.max(y, axis = axis), axis)

    # exponentiate y
    y = np.exp(y)

    # take the sum along the specified axis
    ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)

    # finally: divide elementwise
    p = y / ax_sum

    # flatten if X was 1D
    if len(X.shape) == 1: p = p.flatten()

    return p

Odejmowanie maksimum, jak opisali inni użytkownicy, jest dobrą praktyką. Tutaj napisałem szczegółowy post .

Nolan Conaway
źródło
9

Tutaj możesz dowiedzieć się, dlaczego skorzystali - max.

Stamtąd:

„Kiedy piszesz kod do obliczania funkcji Softmax w praktyce, terminy pośrednie mogą być bardzo duże z powodu wykładniczych. Dzielenie dużych liczb może być niestabilne numerycznie, dlatego ważne jest, aby zastosować sztuczkę normalizacyjną.”

Sadegh Salehi
źródło
4

Bardziej zwięzła wersja to:

def softmax(x):
    return np.exp(x) / np.exp(x).sum(axis=0)
Pimin Konstantin Kefaloukos
źródło
9
może to doprowadzić do przepełnienia arytmetycznego
minhle_r7 18.09.16
4

Aby zaoferować alternatywne rozwiązanie, rozważ przypadki, w których twoje argumenty są bardzo duże, takie, exp(x)że niedopełnienie (w przypadku ujemnym) lub przepełnienie (w przypadku dodatnim). Tutaj chcesz pozostać w przestrzeni dziennika tak długo, jak to możliwe, wykładniczo tylko na końcu, gdzie możesz ufać, że wynik będzie dobrze zachowany.

import scipy.special as sc
import numpy as np

def softmax(x: np.ndarray) -> np.ndarray:
    return np.exp(x - sc.logsumexp(x))
PikalaxALT
źródło
Aby był równy kodowi plakatu, musisz dodać axis=0jako argument do logsumexp.
Björn Lindqvist
Alternatywnie można rozpakować dodatkowe argumenty, aby przekazać je do logsumexp.
PikalaxALT 27.04.2018
3

Potrzebowałem czegoś kompatybilnego z wyjściem gęstej warstwy z Tensorflow .

Rozwiązanie @desertnaut nie działa w tym przypadku, ponieważ mam partie danych. Dlatego przyjechałem z innym rozwiązaniem, które powinno działać w obu przypadkach:

def softmax(x, axis=-1):
    e_x = np.exp(x - np.max(x)) # same code
    return e_x / e_x.sum(axis=axis, keepdims=True)

Wyniki:

logits = np.asarray([
    [-0.0052024,  -0.00770216,  0.01360943, -0.008921], # 1
    [-0.0052024,  -0.00770216,  0.01360943, -0.008921]  # 2
])

print(softmax(logits))

#[[0.2492037  0.24858153 0.25393605 0.24827873]
# [0.2492037  0.24858153 0.25393605 0.24827873]]

Ref: Tensorflow softmax

Lucas Casagrande
źródło
Pamiętaj tylko, że odpowiedź dotyczy bardzo konkretnego ustawienia opisanego w pytaniu; nigdy nie miało to oznaczać „jak obliczyć softmax ogólnie w żadnych okolicznościach lub w formacie danych, który ci się podoba” ...
desertnaut
Rozumiem, umieściłem to tutaj, ponieważ pytanie odnosi się do „klasy głębokiego uczenia Udacity” i nie zadziałałoby, jeśli używasz Tensorflow do budowy swojego modelu. Twoje rozwiązanie jest fajne i czyste, ale działa tylko w bardzo specyficznym scenariuszu. W każdym razie dzięki.
Lucas Casagrande,
2

Sugerowałbym to:

def softmax(z):
    z_norm=np.exp(z-np.max(z,axis=0,keepdims=True))
    return(np.divide(z_norm,np.sum(z_norm,axis=0,keepdims=True)))

Będzie działał zarówno dla partii stochastycznej, jak i wsadowej.
Aby uzyskać więcej informacji, zobacz: https://medium.com/@ravish1729/analysis-of-softmax-function-ad058d6a564d

Ravish Kumar Sharma
źródło
1

Aby zachować stabilność numeryczną, należy odjąć max (x). Poniżej znajduje się kod funkcji softmax;

def softmax (x):

if len(x.shape) > 1:
    tmp = np.max(x, axis = 1)
    x -= tmp.reshape((x.shape[0], 1))
    x = np.exp(x)
    tmp = np.sum(x, axis = 1)
    x /= tmp.reshape((x.shape[0], 1))
else:
    tmp = np.max(x)
    x -= tmp
    x = np.exp(x)
    tmp = np.sum(x)
    x /= tmp


return x
Rahul Ahuja
źródło
1

Już odpowiedziałem bardzo szczegółowo w powyższych odpowiedziach. maxjest odejmowane, aby uniknąć przepełnienia. Dodam tutaj jeszcze jedną implementację w python3.

import numpy as np
def softmax(x):
    mx = np.amax(x,axis=1,keepdims = True)
    x_exp = np.exp(x - mx)
    x_sum = np.sum(x_exp, axis = 1, keepdims = True)
    res = x_exp / x_sum
    return res

x = np.array([[3,2,4],[4,5,6]])
print(softmax(x))
Debashish
źródło
1

Wydaje się, że wszyscy publikują swoje rozwiązania, więc opublikuję moje:

def softmax(x):
    e_x = np.exp(x.T - np.max(x, axis = -1))
    return (e_x / e_x.sum(axis=0)).T

Otrzymuję dokładnie takie same wyniki jak importowane ze sklearn:

from sklearn.utils.extmath import softmax
juliański
źródło
1
import tensorflow as tf
import numpy as np

def softmax(x):
    return (np.exp(x).T / np.exp(x).sum(axis=-1)).T

logits = np.array([[1, 2, 3], [3, 10, 1], [1, 2, 5], [4, 6.5, 1.2], [3, 6, 1]])

sess = tf.Session()
print(softmax(logits))
print(sess.run(tf.nn.softmax(logits)))
sess.close()
Król
źródło
Witamy w SO. Wyjaśnienie, w jaki sposób twój kod odpowiada na pytanie, jest zawsze pomocne.
Nick
1

Na podstawie wszystkich odpowiedzi i notatek CS231n pozwól, że podsumuję:

def softmax(x, axis):
    x -= np.max(x, axis=axis, keepdims=True)
    return np.exp(x) / np.exp(x).sum(axis=axis, keepdims=True)

Stosowanie:

x = np.array([[1, 0, 2,-1],
              [2, 4, 6, 8], 
              [3, 2, 1, 0]])
softmax(x, axis=1).round(2)

Wynik:

array([[0.24, 0.09, 0.64, 0.03],
       [0.  , 0.02, 0.12, 0.86],
       [0.64, 0.24, 0.09, 0.03]])
remykarem
źródło
0

Chciałbym uzupełnić nieco więcej zrozumienia problemu. Tutaj poprawne jest odjęcie maksimum tablicy. Ale jeśli uruchomisz kod w innym poście, okaże się, że nie daje właściwej odpowiedzi, gdy tablica ma wymiary 2D lub wyższe.

Oto kilka sugestii:

  1. Aby uzyskać maksimum, spróbuj zrobić to wzdłuż osi X, otrzymasz tablicę 1D.
  2. Przekształć maksymalną tablicę w oryginalny kształt.
  3. Czy np. Exp uzyskać wartość wykładniczą.
  4. Wykonaj np. Sumę wzdłuż osi.
  5. Uzyskaj ostateczne wyniki.

Postępuj zgodnie z wynikiem, aby uzyskać poprawną odpowiedź, wykonując wektoryzację. Ponieważ jest to związane z pracą domową na uczelni, nie mogę tutaj opublikować dokładnego kodu, ale jeśli nie rozumiesz, chciałbym podać więcej sugestii.

Hao Xu
źródło
1
Nie ma to związku z pracą domową w college'u, a jedynie z quizem nieuczestniczącym na nieakredytowanym kursie, na którym poprawna odpowiedź zostanie podana w następnym kroku ...
desertnaut
0

Funkcja softmax ma na celu zachowanie stosunku wektorów w przeciwieństwie do zgniatania punktów końcowych sigmoidem, gdy wartości są nasycone (tj. Mają tendencję do +/- 1 (tanh) lub od 0 do 1 (logistyka)). Wynika to z faktu, że zachowuje więcej informacji o szybkości zmian w punktach końcowych, a zatem ma większe zastosowanie do sieci neuronowych z kodowaniem wyjściowym 1-z-N (tj. Jeśli zgniecimy punkty końcowe, trudniej będzie odróżnić 1 -of-N klasy wyjściowej, ponieważ nie jesteśmy w stanie stwierdzić, która z nich jest „największa” lub „najmniejsza”, ponieważ została zmiażdżona.); powoduje również, że całkowita suma wyjściowa wynosi 1, a wyraźny zwycięzca będzie bliższy 1, podczas gdy inne liczby, które są blisko siebie, sumują się do 1 / p, gdzie p jest liczbą neuronów wyjściowych o podobnych wartościach.

Odejmowanie maksymalnej wartości od wektora polega na tym, że gdy robisz wykładniki, możesz uzyskać bardzo wysoką wartość, która przycina liczbę zmiennoprzecinkową na maksymalnej wartości, co prowadzi do remisu, czego nie ma w tym przykładzie. Staje się to WIELKIM problemem, jeśli odejmiesz maksymalną wartość, aby uzyskać liczbę ujemną, a następnie masz ujemny wykładnik, który gwałtownie zmniejsza wartości zmieniając stosunek, co wystąpiło w pytaniu plakatu i dało niepoprawną odpowiedź.

Odpowiedź dostarczona przez Udacity jest NAPRAWDĘ nieefektywna. Pierwszą rzeczą, którą musimy zrobić, to obliczyć e ^ y_j dla wszystkich składników wektora, ZACHOWAJ TE WARTOŚCI, następnie zsumuj je i podziel. Tam gdzie Udacity się popsuło, obliczają e ^ y_j dwa razy !!! Oto poprawna odpowiedź:

def softmax(y):
    e_to_the_y_j = np.exp(y)
    return e_to_the_y_j / np.sum(e_to_the_y_j, axis=0)

źródło
0

Celem było osiągnięcie podobnych wyników za pomocą Numpy i Tensorflow. Jedyną zmianą w stosunku do oryginalnej odpowiedzi jest axisparametr dlanp.sum interfejsu API.

Wstępne podejście :axis=0 - Nie zapewnia to jednak zamierzonych wyników, gdy wymiary są N.

Zmodyfikowane podejście : axis=len(e_x.shape)-1- Zawsze sumuj według ostatniego wymiaru. Zapewnia to podobne wyniki jak funkcja softmax tensorflow.

def softmax_fn(input_array):
    """
    | **@author**: Prathyush SP
    |
    | Calculate Softmax for a given array
    :param input_array: Input Array
    :return: Softmax Score
    """
    e_x = np.exp(input_array - np.max(input_array))
    return e_x / e_x.sum(axis=len(e_x.shape)-1)
kingspp
źródło
0

Oto uogólnione rozwiązanie wykorzystujące numpy i porównanie dla poprawności z tensorflow ans scipy:

Przygotowywanie danych:

import numpy as np

np.random.seed(2019)

batch_size = 1
n_items = 3
n_classes = 2
logits_np = np.random.rand(batch_size,n_items,n_classes).astype(np.float32)
print('logits_np.shape', logits_np.shape)
print('logits_np:')
print(logits_np)

Wynik:

logits_np.shape (1, 3, 2)
logits_np:
[[[0.9034822  0.3930805 ]
  [0.62397    0.6378774 ]
  [0.88049906 0.299172  ]]]

Softmax za pomocą tensorflow:

import tensorflow as tf

logits_tf = tf.convert_to_tensor(logits_np, np.float32)
scores_tf = tf.nn.softmax(logits_np, axis=-1)

print('logits_tf.shape', logits_tf.shape)
print('scores_tf.shape', scores_tf.shape)

with tf.Session() as sess:
    scores_np = sess.run(scores_tf)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np,axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Wynik:

logits_tf.shape (1, 3, 2)
scores_tf.shape (1, 3, 2)
scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.4965232  0.5034768 ]
  [0.64137274 0.3586273 ]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]

Softmax za pomocą scipy:

from scipy.special import softmax

scores_np = softmax(logits_np, axis=-1)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np, axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Wynik:

scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.4965232  0.5034768 ]
  [0.6413727  0.35862732]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]

Softmax przy użyciu numpy ( https://nolanbconaway.github.io/blog/2017/softmax-numpy ):

def softmax(X, theta = 1.0, axis = None):
    """
    Compute the softmax of each element along an axis of X.

    Parameters
    ----------
    X: ND-Array. Probably should be floats.
    theta (optional): float parameter, used as a multiplier
        prior to exponentiation. Default = 1.0
    axis (optional): axis to compute values along. Default is the
        first non-singleton axis.

    Returns an array the same size as X. The result will sum to 1
    along the specified axis.
    """

    # make X at least 2d
    y = np.atleast_2d(X)

    # find axis
    if axis is None:
        axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)

    # multiply y against the theta parameter,
    y = y * float(theta)

    # subtract the max for numerical stability
    y = y - np.expand_dims(np.max(y, axis = axis), axis)

    # exponentiate y
    y = np.exp(y)

    # take the sum along the specified axis
    ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)

    # finally: divide elementwise
    p = y / ax_sum

    # flatten if X was 1D
    if len(X.shape) == 1: p = p.flatten()

    return p


scores_np = softmax(logits_np, axis=-1)

print('scores_np.shape', scores_np.shape)
print('scores_np:')
print(scores_np)

print('np.sum(scores_np, axis=-1).shape', np.sum(scores_np, axis=-1).shape)
print('np.sum(scores_np, axis=-1):')
print(np.sum(scores_np, axis=-1))

Wynik:

scores_np.shape (1, 3, 2)
scores_np:
[[[0.62490064 0.37509936]
  [0.49652317 0.5034768 ]
  [0.64137274 0.3586273 ]]]
np.sum(scores_np, axis=-1).shape (1, 3)
np.sum(scores_np, axis=-1):
[[1. 1. 1.]]
mrgloom
źródło
0

Funkcja softmax to funkcja aktywacji, która przekształca liczby w prawdopodobieństwa, które sumują się do jednego. Funkcja softmax wyprowadza wektor, który reprezentuje rozkłady prawdopodobieństwa listy wyników. Jest to również podstawowy element wykorzystywany w zadaniach klasyfikacji w ramach głębokiego uczenia się.

Funkcja Softmax jest używana, gdy mamy wiele klas.

Jest to przydatne do znalezienia klasy, która ma maks. Prawdopodobieństwo.

Funkcja Softmax jest idealnie wykorzystywana w warstwie wyjściowej, gdzie tak naprawdę staramy się osiągnąć prawdopodobieństwo zdefiniowania klasy każdego wejścia.

Wynosi od 0 do 1.

Funkcja Softmax przekształca logi [2,0, 1,0, 0,1] w prawdopodobieństwa [0,7, 0,2, 0,1], a prawdopodobieństwa sumują się do 1. Logity są surowymi wynikami uzyskanymi przez ostatnią warstwę sieci neuronowej. Przed aktywacją. Aby zrozumieć funkcję softmax, musimy spojrzeć na wynik warstwy (n-1).

Funkcja softmax jest w rzeczywistości funkcją arg max. Oznacza to, że nie zwraca największej wartości z wejścia, ale pozycję największych wartości.

Na przykład:

Przed softmax

X = [13, 31, 5]

Po softmax

array([1.52299795e-08, 9.99999985e-01, 5.10908895e-12]

Kod:

import numpy as np

# your solution:

def your_softmax(x): 

"""Compute softmax values for each sets of scores in x.""" 

e_x = np.exp(x - np.max(x)) 

return e_x / e_x.sum() 

# correct solution: 

def softmax(x): 

"""Compute softmax values for each sets of scores in x.""" 

e_x = np.exp(x - np.max(x)) 

return e_x / e_x.sum(axis=0) 

# only difference
krishna veer
źródło