2-qism · Agentlar va bilim izlash·08-bob
Mustahkamlovchi o'qitish
Tajribadan o'rganish: ko'p qo'lli qaroqchi, Markov qarorlari, DQN va PPO — ishlaydigan kod bilan. Va nihoyat, RLHF ichida bularning hammasi qanday birlashadi.
Bolani velosiped haydashga kitob o'qitib o'rgatib bo'lmaydi.
U mingacha marta yiqiladi. Har yiqilganda nimadir o'rganadi: bu tomonga og'sam yiqilaman, tezroq yursam muvozanat yaxshiroq. Hech kim unga har bir harakatni aytib turmaydi — u harakat qiladi, natijani ko'radi, tuzatadi.
Butun kitob davomida biz modelga matn o'qitdik. Mustahkamlovchi o'qitish (RL) — butunlay boshqa yo'l:
Agent muhitda harakat qiladi, natijasini ko'radi va shundan o'rganadi.
RL ning olti qismi#
| Qism | Nima | Shaxmatda |
|---|---|---|
| Agent | Qaror qabul qiluvchi | O'yinchi |
| Muhit | Agent ta'sir qiladigan olam | Doska |
| Holat (state) | Muhitning hozirgi ko'rinishi | Donalar joylashuvi |
| Harakat (action) | Agent qila oladigan narsa | Yurish |
| Siyosat (policy) | Qaysi holatda nima qilish | O'yin strategiyasi |
| Mukofot (reward) | Muhitdan kelgan signal | Dona yeyish / mot |
Va yettinchisi, eng qiyini:
Qiymat funksiyasi — bu holatdan boshlab kelajakda qancha mukofot kutish mumkin.
Ko'p qo'lli qaroqchi#
Eng sodda RL masalasi. Sizda k ta dastagi bor slot mashina. Har bir dastakning o'z
yutish ehtimoli bor, lekin siz uni bilmaysiz.
1000 marta dastak tortishingiz mumkin. Maksimal yutuqni qanday olasiz?
Bu real hayotda ko'p uchraydi: bemorga qaysi dorini berish, A/B test, reklama qaysi variantini ko'rsatish.
Asosiy dilemma: izlash yoki foydalanish#
Izlash (exploration) → yangi dastakni sinab ko'rish. Xavf bor, lekin
eng yaxshisini topish mumkin.
Foydalanish (exploitation) → hozirgi eng yaxshi dastakni tortish. Xavfsiz,
lekin boshqa, yaxshiroq variant qolib ketishi mumkin.
Ikkalasi ham kerak. Buni raqamlar bilan ko'ramiz.
Uch strategiyani solishtiramiz#
import numpy as np
class Qaroqchi:
"""Har dastakning yashirin yutish ehtimoli bor."""
def __init__(self, ehtimolliklar):
self.ehtimolliklar = ehtimolliklar
def tort(self, dastak):
return 1.0 if np.random.random() < self.ehtimolliklar[dastak] else 0.0
def epsilon_greedy(qaroqchi, k, qadamlar, eps):
"""eps ehtimollik bilan tasodifiy, aks holda eng yaxshisini tortadi."""
Q = np.zeros(k) # har dastakning baholangan qiymati
N = np.zeros(k) # har dastak necha marta tortilgan
jami_yutuq = 0
for _ in range(qadamlar):
if np.random.random() < eps:
dastak = np.random.randint(k) # izlash
else:
dastak = int(np.argmax(Q)) # foydalanish
yutuq = qaroqchi.tort(dastak)
N[dastak] += 1
Q[dastak] += (yutuq - Q[dastak]) / N[dastak] # inkremental o'rtacha
jami_yutuq += yutuq
return jami_yutuq
# Tajriba: 10 dastak, biri boshqalardan yaxshiroq
np.random.seed(0)
ehtimolliklar = [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55]
for eps in [0.0, 0.01, 0.1, 0.5]:
yutuqlar = [epsilon_greedy(Qaroqchi(ehtimolliklar), 10, 1000, eps)
for _ in range(100)]
print(f"ε={eps:>4}: o'rtacha yutuq = {np.mean(yutuqlar):.0f} / 1000")ε= 0.0: o'rtacha yutuq = 421 / 1000 ← sof foydalanish
ε=0.01: o'rtacha yutuq = 508 / 1000
ε= 0.1: o'rtacha yutuq = 521 / 1000 ← eng yaxshi
ε= 0.5: o'rtacha yutuq = 388 / 1000 ← juda ko'p izlash
Inkremental yangilash formulasi — butun mashina o'rganishning asosiy naqshi:
Q_yangi = Q_eski + qadam × (mukofot − Q_eski)
└──── xato ────┘
Gradient tushishi ham, keyingi bo'limlar ham xuddi shunga tayanadi: xatoni hisobla, kichik qadam bilan tuzat, takrorla.
Markov qaror jarayonlari#
Qaroqchi masalasida bitta soddalashtirish bor edi: harakat kelajakka ta'sir qilmaydi.
Real dunyoda unday emas. Labirintda chapga burilsangiz — butun keyingi vaziyat o'zgaradi.
MDP aynan shu holatlar uchun. Endi bizga holatga bog'liq qiymat kerak.
Diskont: kelajakni qanchalik qadrlash#
def qaytim(mukofotlar, gamma=0.9):
"""To'plangan diskontlangan mukofot: Gₜ = Rₜ₊₁ + γRₜ₊₂ + γ²Rₜ₊₃ + ..."""
G = 0
for t, r in enumerate(mukofotlar):
G += (gamma ** t) * r
return G
mukofotlar = [1, 1, 1, 1, 1]
for gamma in [0.0, 0.5, 0.9, 0.99]:
print(f"γ={gamma:>5}: qaytim = {qaytim(mukofotlar, gamma):.3f}")γ= 0.0: qaytim = 1.000 ← faqat hozirgi mukofot, ochko'z
γ= 0.5: qaytim = 1.938
γ= 0.9: qaytim = 4.095
γ= 0.99: qaytim = 4.901 ← kelajakni deyarli to'liq hisoblaydi
γ agentning "sabr"ini boshqaradi: nolga yaqin bo'lsa faqat hozirni ko'radi, birga yaqin
bo'lsa uzoq kelajakni ham.
Bellman tenglamasi#
Butun RL shu tenglamaga tayanadi. Uni kodda ko'ramiz — labirint misolida.
import numpy as np
def qiymat_iteratsiyasi(holatlar, harakatlar, otish, mukofot, gamma=0.9, aniqlik=1e-4):
"""
Har holat uchun optimal qiymatni topadi (dynamic programming).
otish[s][a] → [(ehtimol, keyingi_holat), ...]
"""
V = {s: 0.0 for s in holatlar}
while True:
delta = 0
for s in holatlar:
eski = V[s]
# Har harakat uchun kutilayotgan qiymatni hisoblaymiz
qiymatlar = []
for a in harakatlar:
q = sum(
ehtimol * (mukofot(s, a, s2) + gamma * V[s2])
for ehtimol, s2 in otish[s][a]
)
qiymatlar.append(q)
V[s] = max(qiymatlar) # eng yaxshi harakatni tanlaymiz
delta = max(delta, abs(eski - V[s]))
if delta < aniqlik: # yaqinlashdi
break
return VChuqur RL: siyosat o'rnida neyron tarmoq#
Kichik masalada Q qiymatlarini jadvalda saqlash mumkin. Lekin shaxmatda holatlar
soni atomlar sonidan ko'p. Jadval ishlamaydi.
Yechim: neyron tarmoq. U holatni oladi, harakatni chiqaradi.
DQN — Atari o'yinlarini yechgan algoritm#
DeepMind aynan shu bilan mashhur bo'lgan: agent faqat ekran pikselini ko'rib, Atari o'yinlarini odamdan yaxshiroq o'ynashni o'rgandi.
To'liq ishlaydigan DQN:
import torch
import torch.nn as nn
import numpy as np
from collections import deque
import random
class QTarmoq(nn.Module):
def __init__(self, holat_olcham, harakat_soni):
super().__init__()
self.tarmoq = nn.Sequential(
nn.Linear(holat_olcham, 128), nn.ReLU(),
nn.Linear(128, 128), nn.ReLU(),
nn.Linear(128, harakat_soni),
)
def forward(self, x):
return self.tarmoq(x)
class DQN:
def __init__(self, holat_olcham, harakat_soni, gamma=0.99, lr=1e-3):
self.harakat_soni = harakat_soni
self.gamma = gamma
self.eps = 1.0 # boshida ko'p izlaydi
self.q = QTarmoq(holat_olcham, harakat_soni)
self.target = QTarmoq(holat_olcham, harakat_soni)
self.target.load_state_dict(self.q.state_dict())
self.optimizer = torch.optim.Adam(self.q.parameters(), lr=lr)
self.bufer = deque(maxlen=10000) # experience replay
def harakat_tanla(self, holat):
if random.random() < self.eps:
return random.randint(0, self.harakat_soni - 1) # izlash
with torch.no_grad():
q = self.q(torch.tensor(holat, dtype=torch.float32))
return int(q.argmax()) # foydalanish
def eslab_qol(self, *tajriba):
self.bufer.append(tajriba) # (holat, harakat, mukofot, keyingi, tugadi)
def organ(self, paket=64):
if len(self.bufer) < paket:
return
# Tasodifiy paket — ketma-ket bog'liqlikni uzadi
namunalar = random.sample(self.bufer, paket)
holatlar, harakatlar, mukofotlar, keyingilar, tugaganlar = zip(*namunalar)
holatlar = torch.tensor(np.array(holatlar), dtype=torch.float32)
harakatlar = torch.tensor(harakatlar)
mukofotlar = torch.tensor(mukofotlar, dtype=torch.float32)
keyingilar = torch.tensor(np.array(keyingilar), dtype=torch.float32)
tugaganlar = torch.tensor(tugaganlar, dtype=torch.float32)
# Hozirgi Q-qiymat
q_qiymat = self.q(holatlar).gather(1, harakatlar.unsqueeze(1)).squeeze()
# Maqsad: mukofot + γ × keyingi holatning eng yaxshi qiymati
with torch.no_grad():
keyingi_q = self.target(keyingilar).max(1)[0]
maqsad = mukofotlar + self.gamma * keyingi_q * (1 - tugaganlar)
xato = nn.functional.mse_loss(q_qiymat, maqsad)
self.optimizer.zero_grad()
xato.backward()
self.optimizer.step()
def targetni_yangila(self):
self.target.load_state_dict(self.q.state_dict())O'qitish sikli:
import gymnasium as gym
muhit = gym.make("CartPole-v1") # tayoqchani muvozanatda ushlash
agent = DQN(holat_olcham=4, harakat_soni=2)
for epizod in range(300):
holat, _ = muhit.reset()
jami = 0
while True:
harakat = agent.harakat_tanla(holat)
keyingi, mukofot, tugadi, kesildi, _ = muhit.step(harakat)
agent.eslab_qol(holat, harakat, mukofot, keyingi, tugadi or kesildi)
agent.organ()
holat = keyingi
jami += mukofot
if tugadi or kesildi:
break
agent.eps = max(0.05, agent.eps * 0.995) # izlashni asta kamaytiramiz
if epizod % 10 == 0:
agent.targetni_yangila()
print(f"epizod {epizod:>3}: ball = {jami:.0f} ε = {agent.eps:.3f}")Siyosat gradienti va PPO#
DQN qiymatlarni baholaydi. Boshqa oila esa siyosatni to'g'ridan-to'g'ri o'rganadi.
PPO (Proximal Policy Optimization) — bugungi kunda eng ko'p ishlatiladigan RL algoritmi. Uning asosiy g'oyasi bitta:
Siyosat bir qadamda juda ko'p o'zgarmasligi kerak.
import torch
import torch.nn.functional as F
def ppo_xato(eski_ehtimol, yangi_ehtimol, ustunlik, kesim=0.2):
"""
ustunlik — bu harakat kutilganidan qanchalik yaxshi bo'ldi (advantage)
kesim — siyosat qanchalik o'zgarishi mumkin
"""
nisbat = yangi_ehtimol / eski_ehtimol # siyosat qanchalik o'zgardi
# Ikki variant: cheklanmagan va cheklangan
variant1 = nisbat * ustunlik
variant2 = torch.clamp(nisbat, 1 - kesim, 1 + kesim) * ustunlik
# Ehtiyotkorroq (kichikroq)ini olamiz
return -torch.min(variant1, variant2).mean()LLM va RL uchrashadi#
Endi eng qiziq qismi. 3-bobdagi RLHF'ni RL ko'zoynagi bilan qayta ko'ramiz:
Endi RLHF'dagi har bir qism tushunarli:
- KL-divergensiya jazosi — bu aslida PPO'ning "uzoq ketma" cheklovi. Model asl taqsimotdan uzoqlashib ketmasligi kerak.
- Reward hacking — modelning mukofot modelini "aldashi". Yuqorida qayiq misolida ko'rgan narsamiz.
- Mukofot modeli — bu odam afzalliklaridan o'rganilgan qiymat funksiyasi.
CartPole'ni yeching va giperparametrlarni sozlang
Yuqoridagi DQN kodini ishga tushiring va CartPole'ni yeching (100 epizod ketma-ket 195+ ball).
Keyin giperparametrlarni birma-bir sozlab, har birining ta'sirini o'lchang:
gamma: 0.9 vs 0.99 vs 0.999 — qaysi biri tezroq o'rgandi?- Target yangilash chastotasi: har 5, 10, 50 epizod
epskamayish tezligi
Har bir tajriba uchun o'rganish grafigini chizing (epizod → ball).
Savollar:
- Qaysi parametr eng ko'p ta'sir qildi?
- Agent "o'rganib, keyin unutgan" holatni ko'rdingizmi? Nima uchun?
Bonus: LunarLander-v3 — murakkabroq muhit. Bir xil kod ishlaydimi?
Maslahatni ko'rsatish
pip install gymnasium torch Yuqoridagi DQN kodini to'liq ishga tushiring. CartPole 'yechilgan' hisoblanadi: 100 epizod ketma-ket 195+ ball. Sozlang va farqni yozib boring: 1. gamma: 0.9 vs 0.99 vs 0.999 2. Target yangilash chastotasi: har 5 vs 10 vs 50 epizod 3. eps kamayish tezligi: 0.99 vs 0.995 vs 0.999 4. Bufer hajmi: 1000 vs 10000 vs 50000 Grafik chizing: import matplotlib.pyplot as plt plt.plot(ballar) plt.xlabel('epizod'); plt.ylabel('ball') Eng ko'p uchraydigan muammo: agent o'rganib, keyin 'unutadi' (ball tushib ketadi). Bu 'catastrophic forgetting' — bufer hajmini oshiring yoki lr ni kamaytiring. Keyingi qadam: LunarLander-v3 muhitini sinang — murakkabroq.
Xulosa#
- harakat → mukofot
- «o'zim sinab ko'raman»
- yorliq kerak emas
- izlash orqali o'rganadi
- misol → javob
- «menga ko'rsat»
- yorliqlangan ma'lumot kerak
- misollardan o'rganadi
- 01Izlash va foydalanish muvozanati — RL ning yuragi. Faqat foydalansangiz mahalliy optimumda qolasiz, faqat izlasangiz hech qachon foyda ko'rmaysiz. ε-greedy bilan boshlang.
- 02Inkremental yangilash — Q_yangi = Q_eski + qadam × xato. Butun mashina o'rganishning asosiy naqshi, gradient tushishi ham shunga tayanadi.
- 03Mukofot funksiyasi — eng xavfli qism. Agent uni maksimallashtiradi, siz nazarda tutgan narsani emas. Reward hacking real muammo.
- 04DQN'da experience replay va target tarmoq — ikkalasi ham o'qitishni barqaror qiladi. Ularsiz deyarli har doim buziladi.
- 05PPO — amaliyotda asosiy algoritm. «Siyosat bir qadamda ko'p o'zgarmasin» g'oyasi. RLHF aynan shunga tayanadi.
- 06RLHF — bu RL: muhit = foydalanuvchi, agent = LLM, harakat = keyingi token, mukofot = odam afzalliklari. KL jazosi = PPO cheklovi.
Agentimiz endi bilim topishni ham, tajribadan o'rganishni ham biladi. Keyingi qismda ko'lam kengayadi: bitta agent yetarli bo'lmaganda bir nechta agent qanday hamkorlik qiladi va bularning hammasidan haqiqiy mahsulot qanday quriladi.