4-bo‘lim

Faollashtirish funksiyalari

Sigmoid, tanh, ReLU va ularning hosilalari - nima uchun chiziqsizlik bo'lmasa tarmoq ma'nosini yo'qotadi.

🕑 20 daqiqa o‘qish 📄 958 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Chiziqsizlik nima uchun shart
  2. Sigmoid
  3. Sigmoidning hosilasi
  4. Yo'qolayotgan gradient
  5. Tanh
  6. ReLU
  7. O'lik neyronni ko'rish
  8. Xulosa

Oldingi bo'limda neyron faqat to'g'ri chiziq chiza olishini ko'rdik. Faollashtirish funksiyasi shu cheklovni yo'q qiladi - u chiziqli natijani egadi.

Chiziqsizlik nima uchun shart #

Ikkita chiziqli qatlamni ketma-ket qo'yamiz:

Python
import numpy as np
Python
X = np.array([[1.0, 2.0], [3.0, -1.0], [0.5, 0.5]])

W1 = np.array([[0.5, -0.2, 0.3], [0.1, 0.4, -0.6]])
b1 = np.array([0.1, 0.0, -0.1])
W2 = np.array([[1.0], [-0.5], [0.8]])
b2 = np.array([0.2])

ikki_qatlam = (X @ W1 + b1) @ W2 + b2

# xuddi shu narsani bitta qatlamga siqamiz
W_teng = W1 @ W2
b_teng = b1 @ W2 + b2
bitta_qatlam = X @ W_teng + b_teng

print("ikki qatlam:  ", np.round(ikki_qatlam.ravel(), 6))
print("bitta qatlam: ", np.round(bitta_qatlam.ravel(), 6))
print("bir xilmi:", np.allclose(ikki_qatlam, bitta_qatlam))
Natija
ikki qatlam:   [-0.1   3.32  0.35]
bitta qatlam:  [-0.1   3.32  0.35]
bir xilmi: True
Faollashtirishsiz chuqurlik behuda

Yuqoridagi natija butun sohaning asosiy faktini ko'rsatadi: chiziqli qatlamlar zanjiri - baribir bitta chiziqli qatlam.

Yuz qatlam qo'ysangiz ham, ular bitta matritsaga siqiladi:

Natija
(X @ W1) @ W2 @ W3 ... @ Wn  =  X @ (W1 @ W2 @ W3 ... @ Wn)

Ya'ni chiziqsizliksiz "chuqur o'qitish" degan narsa umuman mavjud emas - qanchalik chuqur bo'lmasin, tarmoq 3-bo'limdagi bitta chiziqdan nariga o'ta olmaydi.

Faollashtirish funksiyasi - shunchaki qo'shimcha emas, u butun g'oyaning sharti.

Sigmoid #

Natija
σ(z) = 1 / (1 + e^(-z))
Python
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

z = np.array([-6.0, -2.0, -0.5, 0.0, 0.5, 2.0, 6.0])
print(np.round(sigmoid(z), 4))
Natija
[0.0025 0.1192 0.3775 0.5    0.6225 0.8808 0.9975]
XususiyatQiymat
Chiqish oralig'i(0, 1)
z = 0 daAynan 0.5
Katta musbat z1 ga yaqinlashadi
Katta manfiy z0 ga yaqinlashadi
Ma'nosiEhtimollik kabi o'qiladi
Uchta faollashtirish funksiyasi sigmoid 1 0 (0, 1) oralig'i tanh 1 0 -1 (-1, 1) - markazi nolda ReLU manfiyda 0, musbatda z Qaysi birini qachon sigmoid - faqat CHIQISH qatlamida, ikkilik tasniflash uchun. tanh - sigmoiddan yaxshiroq, lekin bugun kam ishlatiladi. ReLU - YASHIRIN qatlamlar uchun standart tanlov.
Uchtasi ham chiziqli z ni egadi, lekin har xil shaklda
np.exp katta manfiy sonda toshib ketadi

Sodda yozuv katta manfiy z da ogohlantirish beradi:

Python
sigmoid(np.array([-1000.0]))

np.exp(1000) cheksizlikka aylanadi. Natija 0.0 bo'lib chiqadi - to'g'ri javob - lekin yo'lda RuntimeWarning paydo bo'ladi va ba'zi holatlarda nan ham.

Barqaror yozuv ikki shoxga bo'linadi:

Python
def sigmoid_barqaror(z):
    musbat = z >= 0
    natija = np.empty_like(z, dtype=np.float64)
    natija[musbat] = 1.0 / (1.0 + np.exp(-z[musbat]))
    eksp = np.exp(z[~musbat])
    natija[~musbat] = eksp / (1.0 + eksp)
    return natija

Bu 18-bo'limdagi softmax uchun ham bir xil naqsh: eksponenta bilan ishlaganda doim toshib ketishni o'ylang.

Python
def sigmoid_barqaror(z):
    musbat = z >= 0
    natija = np.empty_like(z, dtype=np.float64)
    natija[musbat] = 1.0 / (1.0 + np.exp(-z[musbat]))
    eksp = np.exp(z[~musbat])
    natija[~musbat] = eksp / (1.0 + eksp)
    return natija

z = np.array([-1000.0, -10.0, 0.0, 10.0, 1000.0])
print(np.round(sigmoid_barqaror(z), 6))
print("nan bormi:", bool(np.isnan(sigmoid_barqaror(z)).any()))
Natija
[0.00000e+00 4.50000e-05 5.00000e-01 9.99955e-01 1.00000e+00]
nan bormi: False

Sigmoidning hosilasi #

Gradient tushish uchun hosila kerak bo'ladi (7-bo'lim). Sigmoidda u juda chiroyli:

Natija
σ'(z) = σ(z) · (1 - σ(z))
Python
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_hosila(z):
    s = sigmoid(z)
    return s * (1.0 - s)

z = np.array([-6.0, -2.0, 0.0, 2.0, 6.0])
print("sigmoid: ", np.round(sigmoid(z), 4))
print("hosila:  ", np.round(sigmoid_hosila(z), 4))
Natija
sigmoid:  [0.0025 0.1192 0.5    0.8808 0.9975]
hosila:   [0.0025 0.105  0.25   0.105  0.0025]

Hosila z = 0 da eng katta (0.25) va chetlarda deyarli nolga tushadi.

Buni sonli hosila bilan tekshiramiz:

Python
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_hosila(z):
    s = sigmoid(z)
    return s * (1.0 - s)

z = np.array([-2.0, -0.5, 0.0, 1.0, 3.0])
h = 1e-6
sonli = (sigmoid(z + h) - sigmoid(z - h)) / (2 * h)

print("formula:", np.round(sigmoid_hosila(z), 8))
print("sonli:  ", np.round(sonli, 8))
print("mos keladi:", np.allclose(sigmoid_hosila(z), sonli))
Natija
formula: [0.10499359 0.23500371 0.25       0.19661193 0.04517666]
sonli:   [0.10499359 0.23500371 0.25       0.19661193 0.04517666]
mos keladi: True
Sonli hosila - eng foydali nosozlik topish usuli

Yuqoridagi tekshiruv gradient tekshiruvi (*gradient checking*) deb ataladi va u 11-bo'limda butun tarmoq uchun ishlatiladi.

G'oyasi: hosilaning ta'rifi - kichik h uchun

Natija
f'(x) ≈ (f(x + h) - f(x - h)) / (2h)

Agar sizning formula bo'yicha hisoblagan gradientingiz shu sonli qiymatga mos kelmasa - formulada xato bor.

h qiymatiMuammo
Juda katta (1e-2)Yaqinlashish aniq emas
1e-6Odatda eng yaxshi muvozanat
Juda kichik (1e-12)Suzuvchi nuqta xatosi ustun keladi

Markaziy farq (f(x+h) - f(x-h)) oldinga farqdan (f(x+h) - f(x)) ancha aniqroq - shuning uchun doim shuni ishlating.

Yo'qolayotgan gradient #

Python
def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_hosila(z):
    s = sigmoid(z)
    return s * (1.0 - s)

for z in [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]:
    h = float(sigmoid_hosila(np.array([z]))[0])
    print(f"z = {z:>4.1f}   hosila = {h:.8f}")
Natija
z =  0.0   hosila = 0.25000000
z =  2.0   hosila = 0.10499359
z =  4.0   hosila = 0.01766271
z =  6.0   hosila = 0.00246651
z =  8.0   hosila = 0.00033524
z = 10.0   hosila = 0.00004540

z = 10 da hosila 0.000045 - deyarli nol.

Yo'qolayotgan gradient - chuqur tarmoqlarning kasalligi

Orqaga tarqalishda gradientlar ko'paytiriladi (9-bo'lim). Har qatlam sigmoid ishlatsa, har qadamda gradient eng ko'pi bilan 0.25 ga ko'payadi.

O'nta qatlamda:

Natija
0.25¹⁰ ≈ 0.00000095

Birinchi qatlamlarga yetib boradigan gradient shunchalik kichikki, ular umuman o'qimaydi.

Bu muammo 1990-yillarda chuqur tarmoqlarni amalda qo'llanmaydigan qilib qo'ydi. Uch yechim topildi:

YechimBo'lim
ReLU faollashtirishShu bo'lim
To'g'ri boshlash (Xavier, He)13
Normalizatsiya va qoldiq ulanishlarIlg'or mavzular

Amaliy xulosa: yashirin qatlamlarda sigmoid ishlatmang.

Python
print("0.25 ** 10 =", 0.25 ** 10)
print("0.25 ** 20 =", 0.25 ** 20)
Natija
0.25 ** 10 = 9.5367431640625e-07
0.25 ** 20 = 9.094947017729282e-13

Tanh #

Natija
tanh(z) = (e^z - e^(-z)) / (e^z + e^(-z))
Python
z = np.array([-3.0, -1.0, 0.0, 1.0, 3.0])
print("tanh:  ", np.round(np.tanh(z), 4))
print("hosila:", np.round(1.0 - np.tanh(z) ** 2, 4))
Natija
tanh:   [-0.9951 -0.7616  0.      0.7616  0.9951]
hosila: [0.0099 0.42   1.     0.42   0.0099]
Sigmoid bilan taqqoslashtanh
Oralig'i(-1, 1) - markazi nolda
Maksimal hosila1.0 (sigmoidda 0.25)
Yo'qolayotgan gradientBor, lekin kamroq
"Markazi nolda" nima uchun yaxshi

Sigmoid chiqishi doim musbat (0 dan 1 gacha). Bu keyingi qatlamning barcha kirishlari musbat bo'lishini anglatadi.

Natijada gradientlar ham bir xil ishorada bo'ladi va og'irliklar zigzag bilan yangilanadi - to'g'ridan-to'g'ri emas.

tanh chiqishi esa -1 dan 1 gacha, o'rtacha qiymati nolga yaqin. Bu o'qitishni sezilarli tezlashtiradi.

Xuddi shu sabab 17-bo'limda ma'lumotni normallashtirish tavsiya etiladi: kirishlarni ham nolga markazlash kerak.

ReLU #

Natija
ReLU(z) = max(0, z)
Python
def relu(z):
    return np.maximum(0.0, z)

def relu_hosila(z):
    return (z > 0).astype(np.float64)

z = np.array([-3.0, -0.5, 0.0, 0.5, 3.0])
print("relu:  ", relu(z))
print("hosila:", relu_hosila(z))
Natija
relu:   [0.  0.  0.  0.5 3. ]
hosila: [0. 0. 0. 1. 1.]
AfzalligiIzoh
Hosila 1 yoki 0Gradient yo'qolmaydi
Juda tezFaqat taqqoslash, eksponenta yo'q
SiyraklikNeyronlarning bir qismi nolga tushadi
Python
def relu(z):
    return np.maximum(0.0, z)

def sigmoid_hosila_qiymati(z):
    s = 1.0 / (1.0 + np.exp(-z))
    return s * (1.0 - s)

z = 8.0
print("sigmoid hosilasi z=8 da:", f"{sigmoid_hosila_qiymati(z):.8f}")
print("ReLU hosilasi z=8 da:   ", float(z > 0))
print("10 qatlamdan keyin sigmoid:", f"{sigmoid_hosila_qiymati(z) ** 10:.2e}")
print("10 qatlamdan keyin ReLU:   ", float(z > 0) ** 10)
Natija
sigmoid hosilasi z=8 da: 0.00033524
ReLU hosilasi z=8 da:    1.0
10 qatlamdan keyin sigmoid: 1.79e-35
10 qatlamdan keyin ReLU:    1.0
ReLU ning o'z muammosi - "o'lik neyronlar"

z < 0 bo'lganda hosila aniq nol. Agar neyron barcha namunalarda manfiy z bersa, uning gradienti doim nol bo'ladi - va u hech qachon o'qimaydi.

Bu "o'lik ReLU" (dying ReLU) muammosi. Odatiy sabab - juda katta o'qitish tezligi (15-bo'lim): bitta katta qadam og'irliklarni shunday siljitadiki, neyron butunlay o'chib qoladi.

Yechimlar:

VariantFormulaIzoh
Leaky ReLUmax(0.01z, z)Manfiyda kichik qiyalik
ELUz yoki α(eᶻ-1)Silliq, sekinroq
GELUSilliq taqribiyTransformerlarda standart

Amalda ReLU ko'p hollarda yetarli. Agar tarmoq o'qimayotgan bo'lsa, "nechta neyron doim nol beryapti?" degan savolni bering - bu tez-tez javob bo'lib chiqadi.

Python
def leaky_relu(z, qiyalik=0.01):
    return np.where(z > 0, z, qiyalik * z)

def leaky_relu_hosila(z, qiyalik=0.01):
    return np.where(z > 0, 1.0, qiyalik)

z = np.array([-3.0, -0.5, 0.5, 3.0])
print("leaky relu:", leaky_relu(z))
print("hosila:    ", leaky_relu_hosila(z))
Natija
leaky relu: [-0.03  -0.005  0.5    3.   ]
hosila:     [0.01 0.01 1.   1.  ]

O'lik neyronni ko'rish #

Python
gen = np.random.default_rng(7)
X = gen.normal(0, 1, (200, 10))

# ataylab yomon boshlangan qatlam: barcha siljishlar juda manfiy
W = gen.normal(0, 0.5, (10, 8))
b = np.full(8, -5.0)

A = np.maximum(0.0, X @ W + b)
olik = (A == 0).all(axis=0)

print("har neyron uchun nolga teng chiqishlar ulushi:")
print(np.round((A == 0).mean(axis=0), 3))
print("butunlay o'lik neyronlar soni:", int(olik.sum()), "/ 8")
Natija
har neyron uchun nolga teng chiqishlar ulushi:
[0.995 1.    1.    0.995 1.    1.    1.    1.   ]
butunlay o'lik neyronlar soni: 6 / 8

Sakkiz neyrondan oltitasi barcha 200 namunada nol qaytardi - ular o'qish imkoniyatini butunlay yo'qotgan. Qolgan ikkitasi ham deyarli o'lik: ular namunalarning 99.5% ida nol beradi.

Amaliy topshiriq
  1. Ikkita chiziqli qatlamni bitta matritsaga siqing va tenglikni tasdiqlang.
  2. Sigmoidni yozib, [-6, 6] oralig'ida qiymatlarini chop eting.
  3. sigmoid(-1000) ni sodda va barqaror yozuvda solishtiring.
  4. Sigmoid hosilasini sonli hosila bilan tekshiring.
  5. 0.25 ** 10 ni hisoblang va yo'qolayotgan gradientni tushuntiring.
  6. tanh va uning hosilasini chop eting.
  7. Nima uchun "markazi nolda" bo'lish yaxshi ekanini ayting.
  8. ReLU va uning hosilasini yozing.
  9. Leaky ReLU ni yozib, manfiy qiymatdagi farqni ko'ring.
  10. Siljishni -5 qilib, o'lik neyronlar sonini hisoblang.

Xulosa #

  • Faollashtirishsiz yuz qatlam ham bitta qatlamga siqiladi.
  • Sigmoid (0, 1) beradi - ehtimollik kabi o'qiladi.
  • np.exp katta manfiy sonda toshadi - barqaror yozuvni biling.
  • σ'(z) = σ(z)(1 - σ(z)) - hosila chiqish orqali ifodalanadi.
  • Sigmoid hosilasi eng ko'pi bilan 0.25.
  • Sonli hosila ((f(x+h)-f(x-h))/2h) - formulani tekshirish usuli.
  • 0.25¹⁰ ≈ 1e-6 - yo'qolayotgan gradient.
  • tanh markazi nolda va hosilasi 1 gacha - sigmoiddan yaxshiroq.
  • ReLU hosilasi 1 yoki 0 - gradient yo'qolmaydi.
  • ReLU o'lishi mumkin - Leaky ReLU yoki kichikroq o'qitish tezligi.

Keyingi bo'limda bu qismlarni birlashtiramiz va to'liq oldinga tarqalishni quramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.