aidoc.uz

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#

QismNimaShaxmatda
AgentQaror qabul qiluvchiO'yinchi
MuhitAgent ta'sir qiladigan olamDoska
Holat (state)Muhitning hozirgi ko'rinishiDonalar joylashuvi
Harakat (action)Agent qila oladigan narsaYurish
Siyosat (policy)Qaysi holatda nima qilishO'yin strategiyasi
Mukofot (reward)Muhitdan kelgan signalDona yeyish / mot

Va yettinchisi, eng qiyini:

Qiymat funksiyasi — bu holatdan boshlab kelajakda qancha mukofot kutish mumkin.

holat Sₜ
agent muhitni ko'radi
harakat Aₜ
siyosatga ko'ra tanlaydi
mukofot Rₜ
muhit signal beradi
yangi holat Sₜ₊₁
va sikl takrorlanadi
RL sikli: har qadamda holat → harakat → mukofot → yangi holat

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 V

Chuqur 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.

Q-tarmoq
holatni oladi, har harakat uchun Q-qiymat chiqaradi
asosiy agent
Experience replay
tajribalar buferga saqlanadi, tasodifiy paket olinadi
barqarorlik
Target tarmoq
Q-tarmoqning nusxasi, maqsad qiymatlarni beradi
sekin yangilanadi
DQN'ning uch komponenti

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:

Muhit
foydalanuvchi so'rovi
state
Agent
LLM
policy
Harakat
keyingi tokenni tanlash
action
Mukofot
mukofot modeli bergan ball (odam afzalliklaridan)
reward
Algoritm
PPO
optimizer
RLHF — bu RL, faqat muhit o'rnida foydalanuvchi

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.
Amaliy topshiriq

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:

  1. gamma: 0.9 vs 0.99 vs 0.999 — qaysi biri tezroq o'rgandi?
  2. Target yangilash chastotasi: har 5, 10, 50 epizod
  3. eps kamayish 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#

Mustahkamlovchi (RL)
  • harakat → mukofot
  • «o'zim sinab ko'raman»
  • yorliq kerak emas
  • izlash orqali o'rganadi
Nazorat ostida
  • misol → javob
  • «menga ko'rsat»
  • yorliqlangan ma'lumot kerak
  • misollardan o'rganadi
Nazorat ostida vs mustahkamlovchi o'qitish
Yodda saqlang
  • 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.