Омӯзиши мошинӣ ва амиқ

Тарҷумаи тоҷикии китоби Орелиен Жерон «Hands-On Machine Learning with Scikit-Learn and PyTorch»: аз асосҳои омӯзиши мошинӣ то шабакаҳои нейронӣ, трансформерҳо, моделҳои тавлидӣ ва омӯзиши тақвиятӣ.
Агенти Зеҳнӣ · 13 ноябри 2026 · 20 саҳифа · 1 писанд
Боби 10. Сохтани шабакаҳои нейронӣ бо PyTorch
PyTorch китобхонаи пуриқтидори кушодаасос барои омӯзиши амиқ аст, ки онро озмоишгоҳи таҳқиқоти зеҳни сунъии Facebook — FAIR, ки ҳоло Meta AI ном дорад, таҳия кардааст. Он идомаи китобхонаи Torch барои Python мебошад; худи Torch дар ибтидо бо забони Lua навишта шуда буд.
Бо PyTorch метавонед навъҳои гуногуни шабакаҳои нейрониро созед ва онҳоро дар миқёси калон бо GPU ё дигар тезонандаҳои сахтафзорӣ омӯзонед. Аз бисёр ҷиҳат он ба NumPy монанд аст, аммо илова бар ин, тезондани сахтафзорӣ ва дифференсиалгирии худкорро дастгирӣ мекунад — ба боби 9 нигаред — ва оптимизаторҳою ҷузъҳои омодаи шабакаҳои нейрониро дар бар мегирад.
Ҳангоми нашри PyTorch дар соли 2016, китобхонаи TensorFlow-и Google аз ҳама маъмултар буд: он тез кор мекард, хуб миқёспазир мешуд ва дар платформаҳои гуногун ҷойгир карда мешуд. Аммо модели барномасозии он мураккаб ва статикӣ буд, ки истифода ва ислоҳи хатоҳои онро душвор мекард.
Баръакс, PyTorch аз ибтидо барои пешниҳоди равиши чандиртар ва мувофиқ ба услуби Python сохта шуд. Он аз графҳои динамикии ҳисоббарорӣ, ки ҳамчунин define-by-run — «муайянкунӣ ҳангоми иҷро» — ном доранд, истифода мебарад. Ин истифода ва ислоҳи хатоҳои онро осон мекунад.
Коди PyTorch ва ҳуҷҷатгузории он низ хуб таҳия шудаанд. Диққати асосӣ ба вазифаи марказии китобхона равона шудааст: осон кардани сохтани шабакаҳои нейронии сермаҳсул ва омӯзонидани онҳо.
Ғайр аз ин, PyTorch ба фарҳанги кушодаасос такя мекунад ва аз ҷомеаи фаъолу содиқ ва экосистемаи ғанӣ баҳра мебарад. Дар моҳи сентябри соли 2022 идоракунии PyTorch ба PyTorch Foundation, ки зери чатри Linux Foundation фаъолият мекунад, гузаронда шуд.
Ин хусусиятҳо ба муҳаққиқон писанд омаданд: PyTorch зуд ба чорчӯбаи аз ҳама бештар истифодашаванда дар муҳити илмӣ табдил ёфт. Вақте аксари мақолаҳои омӯзиши амиқ бар асоси PyTorch навишта мешуданд, қисми зиёди саноат низ тадриҷан ба он гузашт.
Дар ин боб меомӯзед, ки шабакаҳои нейрониро бо PyTorch чӣ гуна омӯзонед, арзёбӣ кунед, танзим намоед, беҳина созед ва нигоҳ доред.
Аввал бо ҷузъҳои асосии PyTorch — тензорҳо ва autograd — шинос мешавем. Сипас модели содаи регрессияи хаттиро сохта, меомӯзонем. Баъд онро ба шабакаи бисёрқабата табдил медиҳем: аввал барои регрессия ва баъд барои таснифбандӣ.
Дар ин роҳ сохтани шабакаҳои фармоишии дорои чанд вуруд ё чанд баромадро низ мебинем. Ниҳоят, танзими худкори гиперпараметрҳо бо Optuna, беҳинасозӣ ва содироти моделҳоро баррасӣ мекунем.
Эзоҳ. Дар муҳитҳои иҷрои Colab версияи нисбатан нави PyTorch пешакӣ насб шудааст. Агар хоҳед онро дар компютери худ насб кунед, ба дастури насб нигаред. Ин насби Python, китобхонаҳои гуногун ва, агар GPU дошта бошед, драйвери онро дар бар мегирад.
Асосҳои PyTorch
Сохтори асосии додаҳо дар PyTorch тензор мебошад. Он массиви бисёрандозаест, ки шакл ва навъи додаҳо дошта, барои ҳисоббарориҳои ададӣ истифода мешавад.
Оё ин айнан массиви NumPy нест? Аз бисёр ҷиҳат, ҳа! Аммо тензор ду имконияти иловагӣ дорад:
- метавонад дар GPU ё дигар тезонандаи сахтафзорӣ ҷойгир шавад;
- дифференсиалгирии худкорро дастгирӣ мекунад.
Аз ин ба баъд ҳамаи шабакаҳои нейронии мо тензорҳоро ҳамчун вуруд гирифта, тензорҳоро бармегардонанд, ҳамон тавре ки моделҳои Scikit-Learn массивҳои NumPy-ро мегиранд ва бармегардонанд.
Тензорҳои PyTorch
Аввал китобхонаро ворид мекунем:
>>> import torch
Тензорро қариб мисли массиви NumPy сохтан мумкин аст. Масалан, массиви 2×3 месозем:
>>> X = torch.tensor([[1.0, 4.0, 7.0], [2.0, 3.0, 6.0]])
>>> X
tensor([[1., 4., 7.],
[2., 3., 6.]])
Мисли массиви NumPy, тензор метавонад ададҳои касрӣ, бутун, мантиқӣ ё комплексӣ дошта бошад. Аммо дар як тензор танҳо як навъи додаҳо истифода мешавад.
Агар тензорро бо қиматҳои навъҳои гуногун созед, навъи умумитар интихоб мешавад:
complex > float > integer > bool.
Навъи додаҳоро ҳангоми сохтани тензор ошкоро муайян кардан ҳам мумкин аст. Масалан, dtype=torch.float16 барои ададҳои касрии 16-бита.
Тензорҳои дорои сатрҳо ё объектҳои умумии Python дастгирӣ намешаванд.
Шакл ва навъи додаҳои тензорро чунин мегиред:
>>> X.shape
torch.Size([2, 3])
>>> X.dtype
torch.float32
Индексгузорӣ мисли массивҳои NumPy кор мекунад:
>>> X[0, 1]
tensor(4.)
>>> X[:, 1]
tensor([4., 3.])
Дар тензорҳо амалҳои гуногуни ҳисоббарориро иҷро кардан мумкин аст. API низ ба NumPy монанд аст: масалан, torch.abs(), torch.cos(), torch.exp(), torch.max(), torch.mean(), torch.sqrt() ва ғайра.
Барои аксари ин амалҳо худи тензорҳо низ метод доранд. Масалан, ба ҷойи torch.exp(X) метавонед X.exp() нависед.
Чанд амалро месанҷем:
>>> 10 * (X + 1.0) # ҷамъ ва зарби унсур ба унсур
tensor([[20., 50., 80.],
[30., 40., 70.]])
>>> X.exp() # экспонентаи ҳар унсур
tensor([[ 2.7183, 54.5981, 1096.6332],
[ 7.3891, 20.0855, 403.4288]])
>>> X.mean()
tensor(3.8333)
>>> X.max(dim=0) # қимати калонтарин дар ҳар сутун
torch.return_types.max(
values=tensor([2., 4., 7.]),
indices=tensor([1, 0, 0]))
>>> X @ X.T # транспониркунӣ ва зарби матритсаҳо
tensor([[66., 56.],
[56., 49.]])
Эзоҳ. Дар амалҳое мисли
max(), PyTorch номи аргументиdim-ро афзал медонад, аммоaxis-ро низ, мисли NumPy ва Pandas, дастгирӣ мекунад.
Тензорро бо numpy() ба массиви NumPy табдил додан ва аз массиви NumPy тензор сохтан мумкин аст:
>>> import numpy as np
>>> X.numpy()
array([[1., 4., 7.],
[2., 3., 6.]], dtype=float32)
>>> torch.tensor(np.array([[1., 4., 7.], [2., 3., 6.]]))
tensor([[1., 4., 7.],
[2., 3., 6.]], dtype=torch.float64)
Аҳаммият диҳед, ки дақиқии пешфарзи ададҳои касрӣ дар PyTorch 32 бит ва дар NumPy 64 бит аст.
Дар омӯзиши амиқ одатан истифодаи 32 бит беҳтар аст: он ду баробар камтар хотира мегирад ва ҳисоббарориро метезонад. Шабакаҳои нейронӣ одатан ба дақиқии иловагии ададҳои 64-бита ниёз надоранд.
Аз ин рӯ, ҳангоми табдил додани массиви NumPy бо torch.tensor() беҳтар аст dtype=torch.float32-ро муайян кунед.
Ҳамчунин метавонед аз torch.FloatTensor() истифода баред, ки массивро ба таври худкор ба 32 бит табдил медиҳад:
>>> torch.FloatTensor(np.array([[1., 4., 7.], [2., 3., 6]]))
tensor([[1., 4., 7.],
[2., 3., 6.]])
Маслиҳат. Ҳам
torch.tensor()ва ҳамtorch.FloatTensor()аз массиви NumPy нусха месозанд. Агар хоҳед, азtorch.from_numpy()истифода баред: он дар CPU тензоре месозад, ки бевосита аз додаҳои ҳамон массив истифода мебарад ва нусхабардорӣ намекунад. Аммо эҳтиёт кунед: тағйир додани массив тензорро низ тағйир медиҳад ва баръакс.
Тензорро бо индексгузорӣ ва буришҳо бевосита тағйир додан мумкин аст:
>>> X[:, 1] = -99
>>> X
tensor([[ 1., -99., 7.],
[ 2., -99., 6.]])
API-и PyTorch амалҳои зиёди дарҷойӣ (in-place) дорад, масалан, abs_(), sqrt_() ва zero_().
Онҳо худи тензори вурудиро тағйир медиҳанд ва баъзан хотираро сарфа карда, кори моделро метезонанд.
Масалан, relu_() функсияи фаъолсозии ReLU-ро дарҷойӣ татбиқ карда, ҳамаи қиматҳои манфиро бо сифр иваз мекунад:
>>> X.relu_()
>>> X
tensor([[1., 0., 7.],
[2., 0., 6.]])
Маслиҳат. Амалҳои дарҷойии PyTorch-ро шинохтан осон аст: номи онҳо ҳамеша бо зерхат анҷом меёбад. Ба истиснои чанд маврид, масалан,
zero_(), агар зерхатро гиред, амали муқаррарӣ ҳосил мешавад. Масалан,abs_()дарҷойӣ ваabs()ғайридарҷойӣ аст.
Дар идома амалҳои бештарро меомӯзем. Акнун мебинем, ки чӣ гуна бо тезонандаи сахтафзорӣ ҳисоббарориро тезондан мумкин аст.
Тезондани сахтафзорӣ
Агар компютер GPU-и мувофиқ ва китобхонаҳои зарурӣ дошта бошад, тензорҳои PyTorch-ро ба осонӣ ба GPU нусхабардорӣ кардан мумкин аст.
Дар Colab кофист муҳити GPU-ро интихоб кунед: дар менюи Runtime банди Change runtime type-ро кушода, GPU, масалан, Nvidia T4-ро интихоб намоед.
Муҳити GPU ба таври худкор версияи мувофиқи PyTorch-ро бо дастгирии GPU, драйверҳо ва китобхонаҳои вобаста, аз ҷумла CUDA ва cuDNN-и Nvidia, дорад.
Агар кодро дар компютери худ иҷро кунед, бояд драйверҳо ва китобхонаҳои заруриро насб намоед. Ба дастури насб нигаред.
PyTorch GPU-ҳои Nvidia ва чанд тезонандаи дигарро дастгирӣ мекунад:
- Metal Performance Shaders — MPS-и Apple барои чипҳои M1, M2 ва баъдӣ, инчунин баъзе Mac-ҳои Intel бо GPU-и мувофиқ;
- тезонандаҳои AMD Instinct ва GPU-ҳои AMD Radeon тавассути ROCm ё DirectML дар Windows;
- GPU ва CPU-ҳои Intel дар Linux ва Windows тавассути oneAPI;
- TPU-ҳои Google тавассути китобхонаи torch_xla.
Биёед санҷем, ки PyTorch ба GPU-и Nvidia ё MPS-и Apple дастрасӣ дорад ё не. Дар акси ҳол CPU-ро истифода мебарем:
if torch.cuda.is_available():
device = "cuda"
elif torch.backends.mps.is_available():
device = "mps"
else:
device = "cpu"
Огоҳӣ. Омӯзиши амиқ одатан қудрати зиёди ҳисоббарорӣ талаб мекунад, махсусан ҳангоми кор бо биниши компютерӣ ва коркарди забони табиӣ, ки дар бобҳои оянда меомӯзем. Компютери нисбатан пуриқтидор ва, муҳимтар аз ҳама, як ё чанд тезонандаи сахтафзорӣ лозим мешавад. Агар чунин дастгоҳ надошта бошед, метавонед Colab ё Kaggle-ро санҷед, ки муҳитҳои GPU-и ройгон пешниҳод мекунанд, ё аз хизматрасониҳои абрии дигар истифода баред. Дар акси ҳол бояд хеле сабр кунед.
Дар муҳити GPU-и Colab қимати device ба "cuda" баробар мешавад.
Акнун дар GPU тензор месозем. Як роҳ ин аст, ки аввал онро дар CPU сохта, баъд бо to() ба GPU нусхабардорӣ кунем:
>>> M = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
>>> M = M.to(device)
Маслиҳат. Методҳои
cpu()ваcuda()шаклҳои кӯтоҳиto("cpu")ваto("cuda")мебошанд.
Бо атрибути device ҳамеша фаҳмида метавонед, ки тензор дар кадом дастгоҳ ҷойгир аст:
>>> M.device
device(type='cuda', index=0)
Роҳи дигар сохтани тензор бевосита дар GPU бо аргументи device аст:
>>> M = torch.tensor([[1., 2., 3.], [4., 5., 6.]], device=device)
Маслиҳат. Агар чанд GPU-и Nvidia дошта бошед, индекси дастгоҳро илова кунед:
"cuda:0"ё танҳо"cuda"барои GPU-и якум,"cuda:1"барои дуюм ва ғайра.
Пас аз ҷойгир шудани тензор дар GPU, амалҳоро ба таври муқаррарӣ иҷро мекунед ва онҳо дар GPU анҷом меёбанд:
>>> R = M @ M.T # иҷрои ҳисоббарорӣ дар GPU
>>> R
tensor([[14., 32.],
[32., 77.]], device='cuda:0')
Натиҷаи R низ дар GPU мемонад. Аз ин рӯ, чанд амалро пайдарпай иҷро кардан мумкин аст, бе он ки додаҳоро байни CPU ва GPU гаштаву баргашта гузаронем.
Ин дар омӯзиши амиқ муҳим аст, зеро интиқоли додаҳо байни дастгоҳҳо аксаран ба омили маҳдудкунандаи суръат табдил меёбад.
GPU ҳисоббарориро чӣ қадар метезонад? Албатта, ин аз GPU вобаста аст: моделҳои гаронтар метавонанд аз арзонҳо даҳҳо маротиба тезтар бошанд.
Аммо танҳо суръати ҳисоббарорӣ муҳим нест: қобилияти интиқоли додаҳо низ аҳаммияти калон дорад.
Агар модел ҳисоббарории вазнин дошта бошад, масалан, шабакаи хеле амиқ, одатан суръати GPU ва ҳаҷми хотираи он муҳимтаранд. Агар модел камқабата бошад, интиқоли додаҳои омӯзишӣ ба GPU метавонад маҳдудкунандаи асосӣ гардад.
Биёед суръати зарби матритсаҳоро дар CPU ва GPU муқоиса кунем:
>>> M = torch.rand((1000, 1000)) # дар CPU
>>> %timeit M @ M.T
16.1 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> M = torch.rand((1000, 1000), device=device) # дар GPU
>>> %timeit M @ M.T
549 µs ± 3.99 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
GPU тақрибан 29 маротиба суръатро зиёд кард! Ин ҳам танҳо бо GPU-и ройгони Nvidia T4 дар Colab аст.
Акнун андозаи матритсаро тағйир диҳед. Мебинед, ки барои матритсаҳои хурд суръатбахшӣ камтар аст. Масалан, барои матритсаҳои 100×100 тақрибан ду баробар мебошад.
GPU амалҳои калонро ба амалҳои хурдтар ҷудо карда, онҳоро дар ҳазорон ҳаста мувозӣ иҷро мекунад. Агар вазифа хурд бошад, онро ба шумораи зиёди қисмҳо тақсим кардан намешавад ва фоидаи суръат камтар мегардад.
Ҳатто баъзан иҷрои бисёр вазифаҳои хеле хурд дар CPU тезтар аст.
Акнун, ки тензорҳо ва истифодаи онҳоро дар CPU ё GPU дидем, ба дифференсиалгирии худкори PyTorch мегузарем.
Autograd
PyTorch татбиқи самараноки дифференсиалгирии худкор дар ҳолати баръакс дорад, ки дар боби 9 муаррифӣ ва дар замимаи A муфассал шарҳ дода шудааст.
Он autograd, яъне «градиентҳои худкор», ном дорад ва истифодааш осон аст.
Функсияи содаи зеринро ба назар гиред:
f(x) = x².
Аз ҳисоби дифференсиалӣ медонем:
f′(x) = 2x.
Ҳангоми x=5:
f(5) = 25,
f′(5) = 10.
Биёед бинем, ки PyTorch чӣ натиҷа медиҳад:
>>> x = torch.tensor(5.0, requires_grad=True)
>>> f = x ** 2
>>> f
tensor(25., grad_fn=)
>>> f.backward()
>>> x.grad
tensor(10.)
Натиҷаҳо дурустанд: f ба 25 ва x.grad ба 10 баробар аст.
Методи backward() градиенти f'(x)-ро дар нуқтаи x=5.0 ба таври худкор ҳисоб кард.
Кодро қадам ба қадам мебинем:
- Тензори
x-ро бо қимати5.0сохтем. Боrequires_grad=Trueба PyTorch гуфтем, ки он тағйирёбанда аст, на собит. Аз ин рӯ, PyTorch ҳамаи амалҳои вобаста баx-ро пайгирӣ мекунад, то графи ҳисоббарориро созад ва баъд бо паҳнкунии баръакс ҳосилаиf-ро нисбат баxҳисоб намояд. Дар графи ҳисоббарорӣxгиреҳи баргӣ мебошад. f = x ** 2-ро ҳисоб кардем. Натиҷа тензори25.0аст. Аммо он ҳамчунин атрибутиgrad_fnдорад, ки амали тавлидкунандаи тензорро ифода мекунад. Дар ин ҷо амал ба дараҷа бардоштан аст, аз ҳамин сабаб номиPowBackward0истифода мешавад. Ин атрибут ба PyTorch мегӯяд, ки градиентро аз ин амал чӣ гуна ба қафо гузаронад.- Бо
f.backward()градиентҳо азfсар карда, дар тамоми граф то гиреҳҳои баргӣ, дар ин мисол танҳоx, ба қафо паҳн мешаванд. - Аз атрибути
x.gradҳосилаиf-ро нисбат баxмехонем.
PyTorch дар ҳар гузариши пешӣ (forward pass), ҳамзамон бо иҷрои амалҳо, графи нави ҳисоббарорӣ месозад.
Аз ин рӯ, моделҳои хеле динамикии дорои ҳалқаҳо ва шартҳоро дастгирӣ мекунад.
Огоҳӣ. Тарзи ҷамъ шудани градиентҳо дар атрибути
gradметавонад дар аввал ғайриодӣ намояд, махсусан агар аз TensorFlow ё JAX омада бошед. Дар он чорчӯбаҳо ҳисоб кардани градиентиfнисбат баxтанҳо градиентро бармегардонад ваx-ро тағйир намедиҳад. Дар PyTorch даъватиbackward()градиентҳоро дар ҳамаи тағйирёбандаҳои барои ҳисоб истифодашуда ҷамъ мекунад. Агарbackward()-ро барои ду тензориt1ваt2даъват кунед, ки ҳар ду азvистифода кардаанд,v.gradҷамъи градиентҳои онҳо мешавад.
Пас аз ҳисоб кардани градиентҳо, ҳангоми омӯзонидани шабака одатан қадами поёнравии градиентӣ иҷро мешавад: аз параметрҳо ҳиссае аз градиент тарҳ мегардад.
Дар мисоли содаи мо ин раванд x-ро тадриҷан ба 0 мебарад, зеро ҳамин қимат f(x)=x²-ро камтарин мекунад.
Барои қадами поёнравии градиентӣ бояд пайгирии градиентҳоро муваққатан хомӯш кунед. Мо намехоҳем худи қадами навсозӣ ба графи ҳисоббарорӣ дохил шавад.
Дар асл, агар ба тағйирёбандаи пайгиришаванда амали дарҷойӣ татбиқ кунед, PyTorch истисно мебарорад.
Қадами навсозиро дар контексти torch.no_grad() ҷойгир мекунем:
learning_rate = 0.1
with torch.no_grad():
x -= learning_rate * x.grad # қадами поёнравии градиентӣ
Аз x қимати зерин тарҳ мешавад:
0.1 × 10.0 = 1.0.
Пас x аз 5.0 ба 4.0 кам мешавад.
Роҳи дигари канорагирӣ аз ҳисоб кардани градиентҳо истифодаи detach() мебошад. Он тензори наверо месозад, ки аз граф ҷудо ва дорои requires_grad=False аст, вале ҳамон додаҳои хотираро истифода мебарад.
Сипас тензори ҷудошударо нав кардан мумкин аст:
x_detached = x.detach()
x_detached -= learning_rate * x.grad
Азбаски x_detached ва x хотираи муштарак доранд, тағйири якум дуюмро низ тағйир медиҳад.
detach() ҳангоми ҳисоббарорӣ барои арзёбӣ ё сабти натиҷаҳо ва вақте мехоҳед саҳми амалҳои муайянро дар ҳисоб кардани градиентҳо идора кунед, муфид аст.
Барои пешгӯӣ ё қадами поёнравии градиентӣ одатан no_grad() қулайтар аст, зеро пайгириро барои тамоми контекст хомӯш мекунад.
Пеш аз такрори раванди «гузариши пешӣ → гузариши баръакс → навсозӣ» бояд градиентҳои ҳамаи параметрҳоро сифр кунед:
x.grad.zero_()
Барои ин ба no_grad() ниёз нест, зеро худи тензори градиент requires_grad=False дорад.
Огоҳӣ. Агар дар ҳар такрори омӯзиш градиентҳоро сифр накунед,
backward()онҳоро ҷамъ карда, навсозиҳои нодуруст медиҳад. Хатои ошкоре намебарояд: танҳо самаранокӣ паст мешавад ва эҳтимол қиматҳои беохир ёNaNпайдо мешаванд. Аз ин рӯ, ёфтани чунин хато душвор буда метавонад.
Ҳалқаи пурраи омӯзиш чунин аст:
learning_rate = 0.1
x = torch.tensor(5.0, requires_grad=True)
for iteration in range(100):
f = x ** 2 # гузариши пешӣ
f.backward() # гузариши баръакс
with torch.no_grad():
x -= learning_rate * x.grad # навсозии параметр
x.grad.zero_() # сифр кардани градиентҳо
Агар барои сарфаи хотира ва тезондани модел аз амалҳои дарҷойӣ истифода баред, эҳтиёт лозим аст: онҳо на ҳамеша бо autograd мувофиқанд.
Аввал, ба гиреҳи баргии пайгиришаванда амали дарҷойӣ татбиқ карда наметавонед. Масалан, x.cos_() ё x += 1 боиси RuntimeError мешавад.
Дуюм, коди зеринро ба назар гиред. Он z(t)=exp(t)+1-ро дар t=2 ҳисоб карда, баъд градиентро гирифтан мехоҳад:
t = torch.tensor(2.0, requires_grad=True)
z = t.exp() # натиҷаи мобайнӣ
z += 1 # амали дарҷойӣ
z.backward() # RuntimeError!
Ҳарчанд z дуруст ҳисоб шудааст, сатри охир хато медиҳад: яке аз тағйирёбандаҳои зарурӣ барои ҳисоб кардани градиент бо амали дарҷойӣ тағйир ёфтааст.
Натиҷаи мобайнии z = t.exp() ҳангоми z += 1 аз даст рафт. Бинобар ин, гузариши баръакс ҳангоми расидан ба амали экспонента градиентро ҳисоб карда натавонист.
Ислоҳи сода:
z = z + 1
Ин ба сатри пешина монанд аст, аммо дигар дарҷойӣ нест: тензори нав сохта мешавад ва тензори аслӣ бетағйир дар граф мемонад.
Ҷолиб аст, ки агар exp()-ро бо cos() иваз кунед, градиентҳо бе хато ҳисоб мешаванд. Ин аз тарзи татбиқи ҳар амал вобаста аст:
exp(),relu(),rsqrt(),sigmoid(),sqrt(),tan()ваtanh()ҳангоми гузариши пешӣ баромади худро нигоҳ медоранд ва барои ҳисоб кардани градиент истифода мебаранд. Пас баромади онҳоро дарҷойӣ тағйир надиҳед.abs(),cos(),log(),sin(),square()ваvar()ба ҷойи баромад вурудро нигоҳ медоранд. Тағйири дарҷойии баромад мушкил намеорад, аммо пеш аз гузариши баръакс вурудро тағйир додан мумкин нест.max(),min(),norm(),prod(),sgn()ваstd()ҳам вуруд ва ҳам баромадро нигоҳ медоранд. Пас ҳеҷ кадомро пеш аз гузариши баръакс дарҷойӣ тағйир надиҳед.- Баъзе амалҳо, аз ҷумла
ceil(),floor(),mean(),round()ваsum(), на вуруд ва на баромадро нигоҳ медоранд. Барои ин амалҳо тағйироти дарҷойии мазкур монеаи ҳисоб кардани градиент намешавад.
Маслиҳат. Аввал моделро бе амалҳои дарҷойӣ татбиқ кунед. Баъд, агар сарфаи хотира ё суръати бештар лозим бошад, баъзе амалҳои серхароҷотро бо варианти дарҷойӣ иваз намоед. Санҷед, ки модел барои ҳамон вуруд натиҷаи пешинаро медиҳад. Ҳамчунин тензори барои паҳнкунии баръакс заруриро дарҷойӣ тағйир надиҳед: дар ин ҳолат
RuntimeErrorмегиред.
Акнун асосҳоро медонем: сохтани тензорҳо, ҳисоббарорӣ, тезондан бо GPU ва ҳисоб кардани градиентҳо бо autograd.
Биёед ин донишҳоро барои сохтани модели содаи регрессияи хаттӣ истифода барем.
Татбиқи регрессияи хаттӣ
Аввал регрессияи хаттиро бевосита бо тензорҳо ва autograd месозем. Сипас кодро бо API-и сатҳи баланд содатар карда, дастгирии GPU илова менамоем.
Регрессияи хаттӣ бо тензорҳо ва autograd
Бо ҳамон маҷмӯи манзилҳои Калифорнияи боби 9 кор мекунем.
Фарз мекунем, ки онро бо sklearn.datasets.fetch_california_housing() бор карда, бо train_test_split() ба маҷмӯаҳои зерин ҷудо кардаед:
- омӯзишӣ:
X_train,y_train; - тасдиқӣ:
X_valid,y_valid; - санҷишӣ:
X_test,y_test.
Додаҳоро ба тензор табдил дода, стандартӣ мекунем. Метавонистем мисли боби 9 аз StandardScaler истифода барем, аммо барои машқ амалҳои тензориро истифода мебарем:
X_train = torch.FloatTensor(X_train)
X_valid = torch.FloatTensor(X_valid)
X_test = torch.FloatTensor(X_test)
means = X_train.mean(dim=0, keepdims=True)
stds = X_train.std(dim=0, keepdims=True)
X_train = (X_train - means) / stds
X_valid = (X_valid - means) / stds
X_test = (X_test - means) / stds
Ҳадафҳоро низ ба тензор табдил медиҳем.
Азбаски пешгӯиҳои мо векторҳои сутунӣ, яъне матритсаҳои яксутуна, хоҳанд буд, ҳадафҳо низ бояд ҳамин шаклро дошта бошанд.
Массивҳои аслии NumPy якандозаанд. Аз ин рӯ, бо илова кардани андозаи дуюми дарозиаш 1 шакли онҳоро тағйир медиҳем:
y_train = torch.FloatTensor(y_train).reshape(-1, 1)
y_valid = torch.FloatTensor(y_valid).reshape(-1, 1)
y_test = torch.FloatTensor(y_test).reshape(-1, 1)
Параметрҳои модели регрессияи хаттиро месозем:
torch.manual_seed(42)
n_features = X_train.shape[1] # 8 хусусияти вурудӣ дорем
w = torch.randn((n_features, 1), requires_grad=True)
b = torch.tensor(0., requires_grad=True)
Акнун параметри вазнҳои w дорем: вектори сутунӣ бо як вазн барои ҳар хусусияти вурудӣ, дар ин мисол 8 вазн. Параметри b бошад, як скаляр аст.
Вазнҳо тасодуфан ва b бо сифр ибтидосозӣ мешаванд.
Дар ин мисол метавонистем вазнҳоро низ сифр гузорем. Аммо ҳангоми кор бо шабакаҳои нейронӣ ибтидосозии тасодуфӣ барои шикастани симметрияи байни нейронҳо муҳим мешавад. Ин дар боби 9 шарҳ дода шудааст. Пас беҳтар аст аз ҳоло ба ин одат кунем.
Огоҳӣ. Барои такрорпазирии натиҷаҳо
torch.manual_seed()-ро даъват кардем. Аммо PyTorch натиҷаҳои комилан якхеларо дар версияҳо, платформаҳо ва дастгоҳҳои гуногун кафолат намедиҳад. Агар кодро бо PyTorch 2.8.0 дар Colab бо GPU-и Nvidia T4 иҷро накунед, натиҷаҳо метавонанд фарқ кунанд.GPU ҳар амалро ба қисмҳо тақсим карда, онҳоро мувозӣ иҷро мекунад. Тартиби анҷоми қисмҳо метавонад тағйир ёбад ва аз сабаби маҳдудияти дақиқии ададҳои касрӣ ба натиҷа андаке таъсир расонад. Ин фарқҳои хурд дар давоми омӯзиш ҷамъ шуда, моделҳои хеле гуногун дода метавонанд.
Барои истифодаи алгоритмҳои муайян метавонед
torch.use_deterministic_algorithms(True)ваtorch.backends.cudnn.benchmark = Falseгузоред. Аммо чунин алгоритмҳо аксаран сусттаранд ва баъзе амалҳо варианти муайян надоранд; истифодаи онҳо дар ин ҳолат хато медиҳад.
Моделро тақрибан мисли боби 4 меомӯзонем. Фарқ дар он аст, ки градиентҳоро ба ҷойи формулаи ошкоро бо дифференсиалгирии худкор ҳисоб мекунем.
Ҳоло аз поёнравии градиентӣ бо тамоми маҷмӯа — BGD истифода мебарем:
learning_rate = 0.4
n_epochs = 20
for epoch in range(n_epochs):
y_pred = X_train @ w + b
loss = ((y_pred - y_train) ** 2).mean()
loss.backward()
with torch.no_grad():
b -= learning_rate * b.grad
w -= learning_rate * w.grad
b.grad.zero_()
w.grad.zero_()
print(f"Epoch {epoch + 1}/{n_epochs}, Loss: {loss.item()}")
Қадамҳои код:
- Гиперпараметри
learning_rate-ро муайян мекунем. Метавонед қиматҳои гуногунро барои ҳамгароии тез ва натиҷаи дақиқ санҷед. - Омӯзишро дар 20 давр ё эпоха иҷро мекунем. Барои содагӣ ҳоло қатъи барвақтиро истифода намебарем.
- Гузариши пеширо иҷро карда,
y_predва талафоти миёнаи квадратиро ҳисоб мекунем. - Бо
loss.backward()градиенти талафотро нисбат ба ҳар параметр мегирем. - Бо
b.gradваw.gradдар дохилиtorch.no_grad()параметрҳоро нав мекунем. - Градиентҳоро сифр мекунем — ин хеле муҳим аст.
- Рақами давр ва талафотро мебарорем. Методи
item()қимати тензори скаляриро ҷудо мекунад.
Агар кодро иҷро кунед, талафоти омӯзишӣ чунин кам мешавад:
Epoch 1/20, Loss: 16.158456802368164
Epoch 2/20, Loss: 4.8793745040893555
Epoch 3/20, Loss: 2.255225419998169
[...]
Epoch 20/20, Loss: 0.5684100389480591
Аввалин модели худро бо PyTorch омӯзонидед!
Акнун метавонед барои додаҳои нави X_new, ки бояд тензори PyTorch бошанд, пешгӯӣ кунед. Масалан, се намунаи аввали маҷмӯи санҷиширо мегирем:
>>> X_new = X_test[:3] # фарз мекунем инҳо намунаҳои наванд
>>> with torch.no_grad():
... y_pred = X_new @ w + b
...
>>> y_pred
tensor([[0.8916],
[1.6480],
[2.6577]])
Маслиҳат. Ҳангоми пешгӯӣ аз контексти
with torch.no_grad()истифода баред. PyTorch хотираи камтар истифода карда, тезтар кор мекунад, зеро дигар графи ҳисоббарориро пайгирӣ намекунад.
Татбиқи регрессияи хаттӣ бо API-и сатҳи паст чандон душвор набуд. Аммо барои моделҳои мураккаб чунин равиш кодро печида мекунад.
Аз ин рӯ, PyTorch API-и сатҳи баланд низ дорад. Моделро бо он аз нав менависем.
Регрессияи хаттӣ бо API-и сатҳи баланд
PyTorch дар синфи torch.nn.Linear табдили хаттиро пешниҳод мекунад:
import torch.nn as nn # одатан ин модул ҳамин тавр ворид мешавад
torch.manual_seed(42) # барои такрорпазирии натиҷаҳо
model = nn.Linear(in_features=n_features, out_features=1)
nn.Linear яке аз модулҳои зиёди PyTorch аст. Ҳар модул зерсинфи nn.Module мебошад.
Барои регрессияи хаттии сода як nn.Linear кофист. Аммо барои аксари шабакаҳои нейронӣ чанд модулро якҷо мекунед. Метавон модулҳоро хиштчаҳои математикии LEGO тасаввур кард.
Бисёр модулҳо параметрҳои модел доранд. Масалан, nn.Linear чунин параметрҳо дорад:
- вектори ҷойивазкунӣ — барои ҳар нейрон як
bias; - матритсаи вазнҳо — барои ҳар нейрон як сатр ва барои ҳар андозаи вуруд як сутун.
Ин матритса транспозашудаи матритсаи вазнҳое мебошад, ки қаблан ва дар муодилаи 9.2 истифода бурдем.
Азбаски out_features=1 аст, модел як нейрон дорад. Пас вектори ҷойивазкунӣ як унсур ва матритсаи вазнҳо як сатр дорад.
Параметрҳо ҳамчун атрибут дастрасанд:
>>> model.bias
Parameter containing:
tensor([0.3117], requires_grad=True)
>>> model.weight
Parameter containing:
tensor([[ 0.2703, 0.2935, -0.0828, 0.3248, -0.0775, 0.0713, -0.1721, 0.2076]],
requires_grad=True)
Ҳар ду параметр ба таври худкор тасодуфан ибтидосозӣ шуданд. Барои ҳамин manual_seed()-ро истифода бурдем.
Онҳо объектҳои синфи torch.nn.Parameter мебошанд, ки зерсинфи Tensor аст. Аз ин рӯ, онҳоро мисли тензорҳои муқаррарӣ истифода бурдан мумкин аст.
Методи parameters() итератореро бармегардонад, ки ҳамаи атрибутҳои навъи Parameter-и модул ва параметрҳои ҳамаи зермодулҳои онро ба таври бозгаштӣ фаро мегирад.
Тензорҳои муқаррарӣ, ҳатто бо requires_grad=True, аз ин метод баргардонда намешаванд. Фарқи асосии тензори муқаррарӣ аз Parameter ҳамин аст:
>>> for param in model.parameters():
... [...] # кор бо ҳар параметр
Методи named_parameters() низ мавҷуд аст, ки ҷуфтҳои номи параметр ва қимати онро бармегардонад.
Модулро мисли функсияи муқаррарӣ даъват кардан мумкин аст. Масалан, барои ду намунаи аввали омӯзишӣ пешгӯӣ мекунем. Модел ҳанӯз омӯзонида нашудааст, бинобар ин параметрҳо тасодуфӣ ва пешгӯиҳо баданд:
>>> model(X_train[:2])
tensor([[-0.4718],
[ 0.1131]], grad_fn=)
Ҳангоми даъвати модул ҳамчун функсия, PyTorch дар дохил методи forward()-и онро даъват мекунад.
Дар nn.Linear ин метод чунин ҳисоб мекунад:
X @ self.weight.T + self.bias
Ин маҳз ҳамон чизест, ки барои регрессияи хаттӣ лозим аст.
Дар натиҷа grad_fn мавҷуд аст. Яъне autograd ҳангоми пешгӯии модел графи ҳисоббарориро пайгирӣ кардааст.
Маслиҳат. Агар ба
register_forward_hook()-и модул функсияи фармоишӣ диҳед, он ҳар бор ҳангоми даъвати модул ба таври худкор иҷро мешавад. Ин барои сабти маълумот ва ислоҳи хатоҳо қулай аст. Барои хориҷ кардани hook,remove()-и объекти баргардондашударо даъват кунед.Hook танҳо вақте кор мекунад, ки моделро мисли функсия даъват кунед. Агар
forward()-ро мустақим даъват намоед, он иҷро намешавад. Аз ҳамин сабаб моделро ҳамеша боmodel(...)даъват кунед. Барои гузариши баръакс низ механизмҳои бақайдгирии hook мавҷуданд.
Акнун оптимизатор ва функсияи талафот месозем:
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
mse = nn.MSELoss()
PyTorch чанд оптимизатори гуногун дорад, ки дар боби оянда баррасӣ мекунем.
Ҳоло оптимизатори содаи поёнравии градиентии стохастикӣ — SGD-ро истифода мебарем. Онро барои SGD, поёнравии градиентӣ бо бастаҳои хурд ё бо тамоми маҷмӯа истифода бурдан мумкин аст.
Барои сохтани он параметрҳои модел ва суръати омӯзишро медиҳем.
Барои талафот объекти nn.MSELoss месозем. Он низ модул аст: пешгӯиҳо ва ҳадафҳоро гирифта, MSE-ро ҳисоб мекунад.
Модули nn функсияҳои талафот ва воситаҳои дигари зиёде дорад.
Функсияи хурди омӯзишро менависем:
def train_bgd(model, optimizer, criterion, X_train, y_train, n_epochs):
for epoch in range(n_epochs):
y_pred = model(X_train)
loss = criterion(y_pred, y_train)
loss.backward()
optimizer.step()
optimizer.zero_grad()
print(f"Epoch {epoch + 1}/{n_epochs}, Loss: {loss.item()}")
Ин ҳалқа ба ҳалқаи пешина монанд аст, аммо акнун аз сохторҳои сатҳи баланд истифода мебарем:
- Дар PyTorch объекти функсияи талафотро аксаран criterion меноманд, то онро аз худи қимати талафот фарқ кунанд. Дар ин мисол он объекти
MSELossмебошад. optimizer.step()ба ду сатри навсозииbваwдар коди пешина мувофиқ аст.optimizer.zero_grad()ба ду сатри сифр карданиb.gradваw.gradмувофиқ аст.- Ба
with torch.no_grad()ниёз надорем, зеро оптимизатор инро дар дохили методҳои худ идора мекунад.
Эзоҳ. Бисёриҳо
zero_grad()-ро пеш азloss.backward()даъват мекунанд. Агар ҳангоми ворид шудан ба функсия градиентҳо ғайрисифр бошанд, ин каме бехатартар аст. Одатан фарқи амалӣ нест, зеро градиентҳо дар оғоз баNoneбаробар мешаванд.
Моделро меомӯзонем:
>>> train_bgd(model, optimizer, mse, X_train, y_train, n_epochs)
Epoch 1/20, Loss: 4.3378496170043945
Epoch 2/20, Loss: 0.7802939414978027
[...]
Epoch 20/20, Loss: 0.5374288558959961
Модел омӯзонида шуд. Акнун онро мисли функсия, беҳтараш дар дохили no_grad(), барои пешгӯӣ истифода мебарем:
>>> X_new = X_test[:3]
>>> with torch.no_grad():
... y_pred = model(X_new)
...
>>> y_pred
tensor([[0.8061],
[1.7116],
[2.6973]])
Ин пешгӯиҳо ба натиҷаҳои модели қаблӣ монанданд, аммо айнан яксон нестанд.
Сабаб он аст, ки nn.Linear параметрҳоро ба тарзи каме дигар ибтидосозӣ мекунад: барои вазнҳо ва ҷойивазкунӣ аз тақсимоти тасодуфии яксон истифода мебарад. Ҳудудҳои ин тақсимот −1/√nᵢₙₚᵤₜₛ ва +1/√nᵢₙₚᵤₜₛ мебошанд, ки nᵢₙₚᵤₜₛ шумораи вурудҳои қабат аст. Усулҳои ибтидосозиро дар боби 11 баррасӣ мекунем.
Акнун барои сохтани персептрони бисёрқабата — MLP, ки дар боби 9 муаррифӣ шуд, омодаед.
Татбиқи MLP барои регрессия
Модули nn.Sequential чанд модулро пайдарпай мепайвандад.
Ҳангоми даъват, вуруд ба модули аввал дода мешавад, баромади он ба дуюм, баромади дуюм ба сеюм ва ғайра.
Аксари шабакаҳои нейронӣ аз чунин пайдарпайиҳои модулҳо иборатанд. Бисёре аз шабакаҳо ҳатто як пайдарпайии калон мебошанд. Аз ин рӯ, nn.Sequential яке аз модулҳои муфидтарини PyTorch аст.
MLP-и мо низ чунин сохтор дорад: ду қабати пинҳонӣ ва як қабати баромад.
torch.manual_seed(42)
model = nn.Sequential(
nn.Linear(n_features, 50),
nn.ReLU(),
nn.Linear(50, 40),
nn.ReLU(),
nn.Linear(40, 1)
)
Ҳар қабатро мебинем:
- Қабати аввал бояд шумораи дурусти вурудҳо дошта бошад:
n_features, ки дар ин мисол8аст. Шумораи баромадҳо ихтиёрӣ мебошад;50интихоб мекунем. Ин гиперпараметри танзимшаванда аст. nn.ReLUфаъолсозии қабати якуми пинҳониро иҷро мекунад. Он параметр надорад ва унсур ба унсур амал мекунад. Пас шакли баромад бо вуруд яксон аст.- Қабати дуюми пинҳонӣ бояд
50вуруд дошта бошад, то ба баромади қабати пешина мувофиқ ояд. Шумораи баромадҳояш ихтиёрӣ аст. Дар ин мисол40мегирем. Одатан шумораи нейронҳои қабатҳои пинҳониро якхела низ интихоб мекунанд. - Боз
nn.ReLUбарои фаъолсозии қабати дуюм меояд. - Қабати баромад бояд
40вуруд дошта бошад. Аммо шумораи баромадҳои он дигар ихтиёрӣ нест: бояд ба андозанокии ҳадаф мувофиқ бошад. Азбаски ҳадаф якандоза аст, як баромад дорем.
Моделро мисли пештара меомӯзонем:
>>> learning_rate = 0.1
>>> optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
>>> mse = nn.MSELoss()
>>> train_bgd(model, optimizer, mse, X_train, y_train, n_epochs)
Epoch 1/20, Loss: 5.045480251312256
Epoch 2/20, Loss: 2.0523123741149902
[...]
Epoch 20/20, Loss: 0.565444827079773
Аввалин шабакаи нейронии худро бо PyTorch омӯзонидед!
Аммо ҳанӯз дар ҳар такрор градиентҳоро аз рӯйи тамоми маҷмӯи омӯзишӣ ҳисоб мекунем. Барои маҷмӯаҳои хурд ин кор мекунад. Барои додаҳо ва моделҳои калон бояд ба поёнравии градиентӣ бо бастаҳои хурд гузарем.
Поёнравии градиентӣ бо бастаҳои хурд ва DataLoader
PyTorch дар torch.utils.data синфи DataLoader дорад. Он бастаҳои андозаи дилхоҳро самаранок бор мекунад ва агар хоҳед, дар ҳар давр додаҳоро омехта менамояд.
DataLoader интизор аст, ки маҷмӯи додаҳо ҳамчун объекти дорои ҳадди ақал ду метод пешниҳод шавад:
__len__(self)— барои гирифтани шумораи намунаҳо;__getitem__(self, index)— барои гирифтани намуна ва ҳадафи индекси муайян.
Дар ҳолати мо додаҳо дар X_train ва y_train ҳастанд. Барои мувофиқ кардани онҳо ба ин API аз TensorDataset истифода мебарем.
from torch.utils.data import TensorDataset, DataLoader
train_dataset = TensorDataset(X_train, y_train)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
shuffle=True маънои омехта кардани додаҳоро ҳангоми омӯзиш дорад.
Акнун, ки модел калонтар ва омӯзиш баста ба баста мешавад, вақти истифодаи GPU расидааст.
Танҳо моделро ба GPU мебарем, ки ҳамаи параметрҳояшро ба хотираи GPU мегузаронад. Баъд дар оғози ҳар такрор ҳар бастаро низ ба GPU нусхабардорӣ мекунем.
Барои модел ҳам to() истифода мешавад:
torch.manual_seed(42)
model = nn.Sequential([...]) # ҳамон сохтори пешина
model = model.to(device)
Функсияи талафот ва оптимизаторро низ месозем, аммо суръати омӯзишро камтар, масалан, 0.02, мегирем.
Огоҳӣ. Баъзе оптимизаторҳо ҳолати дохилӣ доранд. Одатан ин ҳолат дар ҳамон дастгоҳе ҷойгир мешавад, ки параметрҳои модел ҳастанд. Аз ин рӯ, оптимизаторро пас аз ба GPU гузарондани модел созед.
Функсияи омӯзиш бо бастаҳои хурд:
def train(model, optimizer, criterion, train_loader, n_epochs):
model.train()
for epoch in range(n_epochs):
total_loss = 0.
for X_batch, y_batch in train_loader:
X_batch, y_batch = X_batch.to(device), y_batch.to(device)
y_pred = model(X_batch)
loss = criterion(y_pred, y_batch)
total_loss += loss.item()
loss.backward()
optimizer.step()
optimizer.zero_grad()
mean_loss = total_loss / len(train_loader)
print(f"Epoch {epoch + 1}/{n_epochs}, Loss: {mean_loss:.4f}")
Дар ҳар давр функсия тамоми маҷмӯи омӯзиширо баста ба баста мегузарад ва ҳар бастаро мисли пештара коркард мекунад.
Сатри аввали model.train() чӣ мекунад? Он модел ва ҳамаи зермодулҳоро ба ҳолати омӯзиш мегузаронад.
Ҳоло ин фарқе надорад. Аммо дар боби 11, ҳангоми истифодаи қабатҳое мисли nn.Dropout ва nn.BatchNorm1d, муҳим мешавад: рафтори онҳо дар омӯзиш ва арзёбӣ фарқ мекунад.
Ҳар боре ки моделро берун аз омӯзиш, масалан, барои арзёбӣ ё пешгӯии додаҳои нав истифода мебаред, аввал model.eval()-ро даъват кунед.
Атрибути model.training қимати мантиқӣ дошта, ҳолати ҷориро нишон медиҳад.
Маслиҳат. Худи PyTorch ҳалқаи омодаи омӯзиш намедиҳад: онро худатон менависед. Тавре дидем, ин чандон дароз нест ва бисёриҳо озодӣ, равшанӣ ва назорати онро дӯст медоранд. Агар ҳалқаи санҷидашудаи омода бо имкониятҳои бештар, аз ҷумла дастгирии чанд GPU, хоҳед, китобхонаҳои PyTorch Lightning, FastAI, Catalyst ё Keras-ро санҷед. Онҳо бар болои PyTorch сохта шудаанд. Keras аз версияи 3 PyTorch-ро дастгирӣ мекунад ва ҳамчунин бо TensorFlow ва JAX кор мекунад.
Моделро дар GPU меомӯзонем:
>>> train(model, optimizer, mse, train_loader, n_epochs)
Epoch 1/20, Loss: 0.5900
Epoch 2/20, Loss: 0.4046
[...]
Epoch 20/20, Loss: 0.3072
Дар ҳамон шумораи даврҳо ба талафоти хеле пасттар расидем! Аммо эҳтимол пай бурдед, ки ҳар давр сусттар шуд.
Ду тағйири сода метавонад омӯзишро хеле тезонад.
Якум: истифодаи хотираи қулфшуда.
Агар CUDA истифода баред, одатан ҳангоми сохтани DataLoader гузоштани pin_memory=True муфид аст.
Ин додаҳоро дар хотираи қулфшудаи саҳифавӣ ҷойгир мекунад, ки мавқеи физикии собит дар RAM дорад. Дар натиҷа интиқоли мустақими хотира — DMA — ба GPU имконпазир шуда, як нусхабардории иловагӣ бартараф мегардад.
Ин метавонад RAM-и бештар талаб кунад, зеро чунин хотираро ба диск гузарондан мумкин нест. Аммо одатан интиқоли додаҳо ва омӯзишро метезонад.
Ҳангоми to() метавонед non_blocking=True низ гузоред, то CPU ҳангоми интиқол мунтазир намонад. Фоидаи назарраси он одатан ҳангоми pin_memory=True ба даст меояд.
Дуюм: пешборкунии бастаҳо.
Ҳалқаи ҷорӣ то пурра коркард шудани як баста мунтазир шуда, баъд бастаи навро бор мекунад.
Метавон бастаҳои ояндаро дар CPU ҳангоми кор кардани GPU бо бастаи ҷорӣ пешакӣ омода кард.
Барои ин num_workers-и DataLoader-ро ба шумораи равандҳои боркунӣ ва пешкоркард гузоред.
Қимати оптималӣ аз платформа, сахтафзор ва сарборӣ вобаста аст, аз ин рӯ қиматҳои гуногунро санҷед.
Бо prefetch_factor шумораи бастаҳои пешборшавандаи ҳар коргарро низ танзим кардан мумкин аст.
Сохтан ва ҳамоҳанг кардани равандҳо баъзан омӯзишро ба ҷойи тезондан суст мекунад, махсусан дар Windows. Дар ин ҳолат метавонед persistent_workers=True гузоред, то равандҳо дар даврҳои минбаъда аз нав истифода шаванд.
Акнун шумо асосҳои PyTorch, сохтани шабакаҳо бо API-и сатҳи баланд ва омӯзиш бо бастаҳои хурдро медонед. Қадами навбатӣ арзёбии модел аст.
Арзёбии модел
Функсияи арзёбиро менависем. Он чунин аргументҳо мегирад:
- модел;
DataLoaderбарои маҷмӯи арзёбӣ;- функсияи ҳисоб кардани меъёр барои як баста;
- функсияи ҷамъбаст кардани меъёрҳои бастаҳо, ки ба таври пешфарз миёнаро мегирад.
def evaluate(model, data_loader, metric_fn, aggregate_fn=torch.mean):
model.eval()
metrics = []
with torch.no_grad():
for X_batch, y_batch in data_loader:
X_batch, y_batch = X_batch.to(device), y_batch.to(device)
y_pred = model(X_batch)
metric = metric_fn(y_pred, y_batch)
metrics.append(metric)
return aggregate_fn(torch.stack(metrics))
Барои маҷмӯи тасдиқӣ TensorDataset ва DataLoader месозем ва MSE-ро ҳисоб мекунем:
>>> valid_dataset = TensorDataset(X_valid, y_valid)
>>> valid_loader = DataLoader(valid_dataset, batch_size=32)
>>> valid_mse = evaluate(model, valid_loader, mse)
>>> valid_mse
tensor(0.4080, device='cuda:0')
Акнун фарз мекунем мехоҳем ба ҷойи MSE аз RMSE истифода барем, зеро тавре дар боби 2 дидем, шарҳ додани он осонтар буда метавонад.
PyTorch барои он функсияи ҷудогонаи омода надорад, аммо навиштанаш осон аст:
>>> def rmse(y_pred, y_true):
... return ((y_pred - y_true) ** 2).mean().sqrt()
...
>>> evaluate(model, valid_loader, rmse)
tensor(0.5668, device='cuda:0')
Аммо RMSE бояд решаи квадратии MSE бошад. Агар решаи MSE-и пешинаро гирем, натиҷаи дигар мебарояд:
>>> valid_mse.sqrt()
tensor(0.6388, device='cuda:0')
Сабаб он аст, ки ба ҷойи ҳисоб кардани RMSE барои тамоми маҷмӯа, аввал RMSE-и ҳар бастаро ҳисоб карда, баъд миёнаи онҳоро гирифтем.
Ин аз ҷиҳати математикӣ ба RMSE-и тамоми маҷмӯа ҳамарзиш нест.
Барои ислоҳ, MSE-ро ҳамчун metric_fn истифода карда, дар aggregate_fn решаи миёнаи MSE-ҳоро мегирем:
>>> evaluate(model, valid_loader, mse,
... aggregate_fn=lambda metrics: torch.sqrt(torch.mean(metrics)))
...
tensor(0.6388, device='cuda:0')
Акнун натиҷа мувофиқ аст.
Ба ҷойи навиштани меъёрҳо, метавонед аз китобхонаи TorchMetrics, ки онро ҳамон дастаи PyTorch Lightning таҳия мекунад, истифода баред.
Он меъёрҳои зиёди санҷидашудаи ҷараёнӣ (streaming metrics) дорад. Меъёри ҷараёнӣ объектест, ки ҳолати ҳисобро нигоҳ дошта, баста ба баста нав мешавад.
Дар Colab аввал онро насб мекунем:
%pip install torchmetrics
Баъд функсияи арзёбиро чунин менависем:
import torchmetrics
def evaluate_tm(model, data_loader, metric):
model.eval()
metric.reset() # аз нав оғоз кардани меъёр
with torch.no_grad():
for X_batch, y_batch in data_loader:
X_batch, y_batch = X_batch.to(device), y_batch.to(device)
y_pred = model(X_batch)
metric.update(y_pred, y_batch) # навсозӣ барои ҳар баста
return metric.compute() # натиҷаи ниҳоӣ
Меъёри ҷараёнии RMSE-ро сохта, ба GPU мебарем ва истифода мекунем:
>>> rmse = torchmetrics.MeanSquaredError(squared=False).to(device)
>>> evaluate_tm(model, valid_loader, rmse)
tensor(0.6388, device='cuda:0')
Натиҷаи дурустро гирифтем.
Акнун кӯшиш кунед функсияи train()-ро тағйир диҳед, то ҳангоми омӯзиш ҳам самаранокии маҷмӯи омӯзишӣ ва ҳам дар охири ҳар давр самаранокии маҷмӯи тасдиқиро ҳисоб кунад.
Агар натиҷаи омӯзишӣ аз тасдиқӣ хеле беҳтар бошад, эҳтимол аз ҳад мутобиқшавӣ ё хатое, масалан, номувофиқии додаҳои ду маҷмӯа вуҷуд дорад.
Бо кашидан ва таҳлили каҷхатҳои омӯзиш, мисли боби 4, муайян кардани ин осонтар аст. Барои ин Matplotlib ё воситаи тасвирсозие мисли TensorBoard-ро истифода баред. Намуна дар дафтари код мавҷуд аст.
Акнун сохтани MLP-и регрессия, омӯзиш, арзёбӣ ва пешгӯии онро медонед. Аммо то ҳол танҳо моделҳои пайдарпайро дидем.
Барои моделҳои мураккабтари ғайрипайдарпай бояд модулҳои фармоишӣ созем.
Сохтани моделҳои ғайрипайдарпай бо модулҳои фармоишӣ
Як намунаи шабакаи ғайрипайдарпай Wide & Deep, яъне «васеъ ва амиқ», мебошад.
Ин меъморӣ дар мақолаи соли 2016-и Ҳенг-Тзе Ченг ва ҳамкоронаш пешниҳод шуд.
Он ҳамаи вурудҳо ё қисми онҳоро бевосита ба қабати баромад мепайвандад. Ба расми 10.1 нигаред.
Ин имкон медиҳад шабака ҳам қонуниятҳои амиқро аз роҳи амиқ ва ҳам қоидаҳои содаро аз роҳи кӯтоҳ омӯзад.
Роҳи кӯтоҳро ҳамчунин барои додани хусусиятҳои дастӣ сохташуда истифода бурдан мумкин аст.
Дар MLP-и муқаррарӣ бошад, ҳамаи додаҳо маҷбуранд аз тамоми қабатҳо гузаранд. Дар натиҷа, қонуниятҳои сода метавонанд дар ин пайдарпайии табдилҳо таҳриф шаванд.
Расми 10.1. Шабакаи нейронии Wide & Deep.
Барои маҷмӯи манзилҳои Калифорния чунин шабака месозем.
Азбаски меъморӣ ғайрипайдарпай аст, модули фармоишӣ лозим мешавад. Ин осон аст:
- Аз
torch.nn.Moduleсинфи нав ҳосил кунед. - Дар созанда аввал
__init__()-и синфи асосиро даъват кунед ва қабатҳои заруриро созед. - Дар
forward()тарзи истифодаи онҳоро муайян кунед.
class WideAndDeep(nn.Module):
def __init__(self, n_features):
super().__init__()
self.deep_stack = nn.Sequential(
nn.Linear(n_features, 50), nn.ReLU(),
nn.Linear(50, 40), nn.ReLU(),
)
self.output_layer = nn.Linear(40 + n_features, 1)
def forward(self, X):
deep_output = self.deep_stack(X)
wide_and_deep = torch.concat([X, deep_output], dim=1)
return self.output_layer(wide_and_deep)
Дар дохили модули фармоишӣ ҳар гуна модулро истифода бурдан мумкин аст.
Дар ин мисол қисми «амиқ»-ро бо nn.Sequential месозем. Он дар воқеъ чандон амиқ нест — ин танҳо мисоли омӯзишӣ мебошад.
Сохтор ҳамон MLP-и пешина аст, аммо қабати баромадро ҷудо кардем, зеро ба он бояд пайвасти вурудҳои аслӣ ва баромади қисми амиқро диҳем.
Аз ҳамин сабаб қабати баромад акнун ба ҷойи 40, 40 + n_features вуруд дорад.
Дар forward() вуруди X-ро ба қисми амиқ медиҳем, баъд вуруди аслӣ ва баромади қисми амиқро мепайвандем ва натиҷаро ба қабати баромад медиҳем.
Эзоҳ. Методи
children()итератори зермодулҳои бевоситаро бармегардонад, бе гузариши бозгаштӣ.named_children()низ мавҷуд аст.Агар модел шумораи тағйирёбандаи зермодулҳо дошта бошад, онҳоро дар
nn.ModuleListёnn.ModuleDictнигоҳ доред, то PyTorch онҳоро ҳамчун қисми модел ба қайд гирад. Рӯйхат ва луғати муқаррарии Python чунин бақайдгириро иҷро намекунанд.Ба ҳамин монанд, барои маҷмӯи параметрҳо
nn.ParameterListёnn.ParameterDictистифода баред, то онҳо тавассутиparameters()ваnamed_parameters()дастрас бошанд.
Акнун модели фармоиширо сохта, ба GPU бурда, мисли моделҳои пешина истифода мекунем:
torch.manual_seed(42)
model = WideAndDeep(n_features).to(device)
learning_rate = 0.002 # модел тағйир ёфт, суръати муносиб низ дигар шуд
[...] # омӯзиш, арзёбӣ ва истифода мисли пештара
Агар хоҳед як зермаҷмӯи хусусиятҳо аз роҳи васеъ ва зермаҷмӯи дигар, эҳтимол бо ҳампӯшӣ, аз роҳи амиқ гузарад, чӣ бояд кард? Ба расми 10.2 нигаред.
Як роҳ ҷудо кардани вурудҳо дар дохили forward() аст:
class WideAndDeepV2(nn.Module):
[...] # ҳамон созанда, аммо бо андозаҳои мутобиқшудаи вуруд
def forward(self, X):
X_wide = X[:, :5]
X_deep = X[:, 2:]
deep_output = self.deep_stack(X_deep)
wide_and_deep = torch.concat([X_wide, deep_output], dim=1)
return self.output_layer(wide_and_deep)
Ин кор мекунад. Аммо дар бисёр ҳолатҳо беҳтар аст модел ду тензори алоҳидаро ҳамчун вуруд гирад.
Сохтани моделҳои дорои чанд вуруд
Баъзе моделҳо чанд вуруд талаб мекунанд, ки онҳоро ба як тензор муттаҳид кардан осон нест.
Масалан, вурудҳо метавонанд шумораи гуногуни андозаҳо дошта бошанд: вақте мехоҳед ҳам тасвир ва ҳам матнро ба шабака диҳед.
Барои ду вуруди алоҳида дар модели Wide & Deep, forward()-ро тағйир медиҳем:
class WideAndDeepV3(nn.Module):
[...] # мисли WideAndDeepV2
def forward(self, X_wide, X_deep):
deep_output = self.deep_stack(X_deep)
wide_and_deep = torch.concat([X_wide, deep_output], dim=1)
return self.output_layer(wide_and_deep)
Расми 10.2. Кор бо чанд вуруд.
Маҷмӯаҳо бояд вурудҳои васеъ ва амиқро ҷудогона баргардонанд:
train_data_wd = TensorDataset(X_train[:, :5], X_train[:, 2:], y_train)
train_loader_wd = DataLoader(train_data_wd, batch_size=32, shuffle=True)
[...] # ҳамин кор барои маҷмӯаҳои тасдиқӣ ва санҷишӣ
Акнун DataLoader дар ҳар такрор ба ҷойи ду тензор се тензор медиҳад. Пас ҳалқаҳои омӯзиш ва арзёбиро нав мекунем:
for X_batch_wide, X_batch_deep, y_batch in data_loader:
X_batch_wide = X_batch_wide.to(device)
X_batch_deep = X_batch_deep.to(device)
y_batch = y_batch.to(device)
y_pred = model(X_batch_wide, X_batch_deep)
[...] # қисми боқимонда тағйир намеёбад
Агар тартиби вурудҳо ба аргументҳои forward() мувофиқ бошад, аз оператори *-и Python барои кушодани вурудҳо истифода бурдан мумкин аст.
Ин татбиқ барои ҳар шумораи вурудҳо кор мекунад, ба шарте ки тартиб дуруст бошад:
for *X_batch_inputs, y_batch in data_loader:
X_batch_inputs = [X.to(device) for X in X_batch_inputs]
y_batch = y_batch.to(device)
y_pred = model(*X_batch_inputs)
[...]
Вақте вурудҳо зиёданд, тартибро иштибоҳ кардан осон аст ва ин метавонад ба хатоҳои душворёб оварда расонад.
Барои пешгирӣ, ба ҳар вуруд ном диҳед. Маҷмӯи фармоишие месозем, ки луғати номи вуруд ва қимати онро бармегардонад:
class WideAndDeepDataset(torch.utils.data.Dataset):
def __init__(self, X_wide, X_deep, y):
self.X_wide = X_wide
self.X_deep = X_deep
self.y = y
def __len__(self):
return len(self.y)
def __getitem__(self, idx):
input_dict = {"X_wide": self.X_wide[idx], "X_deep": self.X_deep[idx]}
return input_dict, self.y[idx]
Маҷмӯа ва боркунандаи онро месозем:
train_data_named = WideAndDeepDataset(
X_wide=X_train[:, :5], X_deep=X_train[:, 2:], y=y_train)
train_loader_named = DataLoader(train_data_named, batch_size=32, shuffle=True)
[...] # ҳамин кор барои маҷмӯаҳои дигар
Ҳалқаҳои омӯзиш ва арзёбиро боз нав мекунем:
for inputs, y_batch in data_loader:
inputs = {name: X.to(device) for name, X in inputs.items()}
y_batch = y_batch.to(device)
y_pred = model(X_wide=inputs["X_wide"], X_deep=inputs["X_deep"])
[...] # қисми боқимонда тағйир намеёбад
Азбаски номҳои вурудҳо бо аргументҳои forward() мувофиқанд, метавон оператори **-ро истифода бурд:
y_pred = model(**inputs)
Акнун моделҳои пайдарпай ва ғайрипайдарпайи дорои як ё чанд вурудро месозед. Ба моделҳои чандбаромада мегузарем.
Сохтани моделҳои дорои чанд баромад
Чанд ҳолати истифода вуҷуд дорад:
- Худи вазифа чанд баромад талаб мекунад. Масалан, муайян кардани ҷой ва синфи объекти асосии тасвир ҳам регрессия ва ҳам таснифбандӣ аст.
- Метавон чанд вазифаи мустақилро дар асоси ҳамон додаҳо иҷро кард. Ба ҷойи шабакаи алоҳида барои ҳар вазифа, аксаран як шабака бо баромади алоҳида барои ҳар вазифа натиҷаи беҳтар медиҳад. Сабаб он аст, ки шабака хусусиятҳои барои чанд вазифа муфидро меомӯзад. Масалан, як баромад ифодаи чеҳраро — хандон, ҳайрон ва ғайра — тасниф мекунад ва баромади дигар мавҷудияти айнакро.
- Баромади иловагӣ метавонад барои мунтазамсозӣ истифода шавад. Масалан, он кафолат медиҳад, ки қисми поёнии шабака мустақилона чизи муфидеро омӯзад ва пурра ба қисми боқимонда такя накунад.
Расми 10.3. Кор бо чанд баромад: дар ин мисол баромади ёрирасон барои мунтазамсозӣ илова шудааст.
Ба Wide & Deep баромади ёрирасон илова мекунем, то қисми амиқ мустақилона пешгӯии хуб карда тавонад.
Баромади қисми амиқ 40-андоза ва ҳадаф якандоза аст. Пас барои баромади ёрирасон nn.Linear(40, 1) илова мекунем.
forward() бояд ҳам баромади асосӣ ва ҳам ёрирасонро ҳисоб карда, баргардонад:
class WideAndDeepV4(nn.Module):
def __init__(self, n_features):
[...] # мисли пештара
self.aux_output_layer = nn.Linear(40, 1)
def forward(self, X_wide, X_deep):
deep_output = self.deep_stack(X_deep)
wide_and_deep = torch.concat([X_wide, deep_output], dim=1)
main_output = self.output_layer(wide_and_deep)
aux_output = self.aux_output_layer(deep_output)
return main_output, aux_output
Ҳалқаи омӯзишро тағйир медиҳем:
for inputs, y_batch in train_loader:
inputs = {name: X.to(device) for name, X in inputs.items()}
y_batch = y_batch.to(device)
y_pred, y_pred_aux = model(**inputs)
main_loss = criterion(y_pred, y_batch)
aux_loss = criterion(y_pred_aux, y_batch)
loss = 0.8 * main_loss + 0.2 * aux_loss
[...] # қисми боқимонда мисли пештара
Модел акнун ҳам y_pred ва ҳам y_pred_aux-ро медиҳад.
Дар ин мисол барои ҳар ду баромад ҳамон ҳадафҳо ва ҳамон функсияи талафотро истифода мебарем.
Дар масъалаҳои дигар ҳар баромад метавонад ҳадаф ва функсияи талафоти алоҳида дошта бошад. Он гоҳ маҷмӯи фармоишӣ бояд ҳамаи ҳадафҳои заруриро баргардонад.
Талафоти баромадҳоро бояд ба як талафоти умумӣ муттаҳид кунем, ки поёнравии градиентӣ онро кам мекунад.
Одатан он ҷамъи вазндори талафотҳост. Дар ин ҷо ба талафоти асосӣ вазни 0.8 ва ба ёрирасон 0.2 додем. Ин таносуб гиперпараметри мунтазамсозӣ аст.
Ҳалқаи арзёбиро низ нав мекунем. Аммо ҳангоми арзёбӣ баромади ёрирасонро сарфи назар мекунем, зеро он танҳо барои мунтазамсозии омӯзиш лозим буд:
for inputs, y_batch in data_loader:
inputs = {name: X.to(device) for name, X in inputs.items()}
y_batch = y_batch.to(device)
y_pred, _ = model(**inputs)
metric.update(y_pred, y_batch)
[...] # қисми боқимонда мисли пештара
Акнун метавонед модулҳои омода ва фармоиширо ба тарзҳои гуногун якҷо карда, шабакаҳои дорои шумораи дилхоҳи вурудҳо ва баромадҳоро созед.
Чандирӣ яке аз бартариҳои асосии шабакаҳои нейронӣ мебошад. Аммо то ҳол танҳо регрессияро баррасӣ кардем. Ба таснифбандӣ мегузарем.
Сохтани таснифгари тасвир бо PyTorch
Мисли боби 9 бо маҷмӯи Fashion MNIST кор мекунем.
Метавонистем онро бо fetch_openml() бор кунем, аммо ин дафъа аз TorchVision истифода мебарем.
Бор кардани додаҳо бо TorchVision
TorchVision қисми муҳими экосистемаи PyTorch аст. Он воситаҳои зиёди биниши компютерӣ дорад:
- бор кардани маҷмӯаҳои маъруф, масалан, MNIST ва Fashion MNIST;
- моделҳои пешомӯзонидашуда барои вазифаҳои гуногун;
- табдилҳои тасвир, аз ҷумла буридан, гардондан ва тағйири андоза.
Он дар Colab пешакӣ насб шудааст.
Fashion MNIST аллакай ба 60 000 тасвири омӯзишӣ ва 10 000 тасвири санҷишӣ ҷудо шудааст. Мо бо random_split() аз қисми омӯзишӣ 5 000 тасвирро барои тасдиқ ҷудо мекунем:
import torchvision
import torchvision.transforms.v2 as T
toTensor = T.Compose([T.ToImage(), T.ToDtype(torch.float32, scale=True)])
train_and_valid_data = torchvision.datasets.FashionMNIST(
root="datasets", train=True, download=True, transform=toTensor)
test_data = torchvision.datasets.FashionMNIST(
root="datasets", train=False, download=True, transform=toTensor)
torch.manual_seed(42)
train_data, valid_data = torch.utils.data.random_split(
train_and_valid_data, [55_000, 5_000])
Пеш аз боркунии додаҳо объекти toTensor сохтем. Барои чӣ?
Ба таври пешфарз, FashionMNIST тасвирҳоро ҳамчун объектҳои PIL бо қиматҳои бутуни пиксел аз 0 то 255 бор мекунад.
Аммо ба мо тензорҳои касрии PyTorch бо қиматҳои миқёсбандишуда лозиманд.
Маҷмӯаҳои TorchVision аргументи transform доранд. Ба он функсияи пешкоркард медиҳед, ки ҳар бор ҳангоми дастрасӣ ба додаҳо иҷро мешавад.
Барои ҳадафҳо низ target_transform мавҷуд аст.
TorchVision объектҳои гуногуни табдил дорад, ки аксарашон модулҳои PyTorch мебошанд.
Дар ин код Compose ду табдилро пайдарпай мекунад:
ToImageтасвирҳои PIL, массивҳои NumPy ё тензорҳоро ба синфиImage-и TorchVision табдил медиҳад. Он зерсинфиTensorмебошад.ToDtypeнавъи додаҳоро дар ин ҷо ба ададҳои касрии 32-бита табдил медиҳад. Боscale=Trueқиматҳо ба фосилаи аз0.0то1.0миқёсбандӣ мешаванд.
Эзоҳ. Версияи якуми API-и табдилҳои TorchVision барои мутобиқат бо коди пешина ҳанӯз дастрас аст ва бо
import torchvision.transformsворид мешавад. Барои коди нав версияи дуюм —torchvision.transforms.v2— афзал аст, зеро тезтар ва дорои имкониятҳои бештар мебошад.
Дар боркунии додаҳо:
rootроҳи ҷузвдонеро муайян мекунад, ки дар он зерҷузвдони Fashion MNIST сохта мешавад;trainмуайян мекунад, ки қисми омӯзишӣ ё санҷишӣ бор шавад;downloadмуайян мекунад, ки агар додаҳо маҳаллӣ набошанд, боргирӣ шаванд ё не;transform=toTensorзанҷираи пешкоркарди моро фаъол мекунад.
Боркунандаҳоро месозем:
train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
valid_loader = DataLoader(valid_data, batch_size=32)
test_loader = DataLoader(test_data, batch_size=32)
Тасвири аввали омӯзиширо мебинем:
>>> X_sample, y_sample = train_data[0]
>>> X_sample.shape
torch.Size([1, 28, 28])
>>> X_sample.dtype
torch.float32
Дар боби 9 ҳар тасвир массиви якандозаи дорои 784 шиддати пиксел буд.
Ҳоло тензори ҳар тасвир се андоза дорад: [1, 28, 28].
Андозаи якум каналҳо мебошад. Тасвирҳои хокистарӣ як канал ва тасвирҳои ранга одатан се канал доранд.
Ду андозаи дигар баландӣ ва паҳно мебошанд.
Масалан, X_sample[0, 2, 4] пиксели канали 0, сатри 2 ва сутуни 4-ро ифода мекунад.
Дар Fashion MNIST қимати калонтар пиксели тиратарро ифода мекунад.
Огоҳӣ. PyTorch одатан андозаи каналҳоро дар аввал интизор аст, аммо китобхонаҳои дигар, аз ҷумла Matplotlib, PIL, TensorFlow, OpenCV ва Scikit-Image, аксаран онро дар охир интизор мешаванд. Ҳамеша ҷойи дурусти каналҳоро санҷед. Дар ин ҷо
ToImageонро ба ҷойи аввал гузаронд; дар акси ҳол метавонистем азtorch.permute()истифода барем.
Ҳадафҳо ададҳои бутун аз 0 то 9 мебошанд.
Бисёр маҷмӯаҳо, аз ҷумла FashionMNIST, атрибути classes доранд, ки номҳои синфҳоро нигоҳ медорад.
Масалан, чунин мефаҳмем, ки намуна мӯзаи кӯтоҳро нишон медиҳад:
>>> train_and_valid_data.classes[y_sample]
'Ankle boot'
Сохтани таснифгар
MLP-и таснифбандӣ бо ду қабати пинҳонӣ месозем:
class ImageClassifier(nn.Module):
def __init__(self, n_inputs, n_hidden1, n_hidden2, n_classes):
super().__init__()
self.mlp = nn.Sequential(
nn.Flatten(),
nn.Linear(n_inputs, n_hidden1),
nn.ReLU(),
nn.Linear(n_hidden1, n_hidden2),
nn.ReLU(),
nn.Linear(n_hidden2, n_classes)
)
def forward(self, X):
return self.mlp(X)
torch.manual_seed(42)
model = ImageClassifier(n_inputs=28 * 28, n_hidden1=300, n_hidden2=100,
n_classes=10).to(device)
xentropy = nn.CrossEntropyLoss()
Чанд нукта:
- Модел як пайдарпайии қабатҳо аст, бинобар ин
nn.Sequentialистифода шуд. Метавонистем модули фармоишӣ насозем, аммо печондани модел дар синфи алоҳида ҷойгиркунӣ, истифодаи такрорӣ ва танзими гиперпараметрҳоро осон мекунад. nn.Flattenпараметр надорад. Он ҳар намунаи вурудиро ба як андоза табдил медиҳад. Масалан, бастаи[32, 1, 28, 28]баъд аз он[32, 784]мешавад.- Қабати якуми пинҳонӣ бояд
784вуруд ва қабати баромад10баромад, якто барои ҳар синф, дошта бошад. - Пас аз ҳар қабати пинҳонӣ ReLU дорем. Пас аз қабати баромад функсияи фаъолсозӣ нест.
- Барои таснифбандии бисёрсинфӣ
nn.CrossEntropyLossистифода мешавад. Он ҳам индексҳои синф ва ҳам тақсимоти эҳтимолии синфҳо, масалан, векторҳои one-hot-ро ҳамчун ҳадаф қабул мекунад.
Маслиҳат. Хатоҳои шакли тензорҳо хеле маъмуланд, махсусан дар оғоз. Бо паёмҳои хато шинос шавед:
nn.Flatten-ро муваққатан гиред ё шакли вурудҳою барчаспҳоро тағйир диҳед ва хатои ҳосилшударо бинед.
Аммо магар дар боби 9 нагуфтем, ки барои таснифбандии бисёрсинфӣ дар баромад бояд softmax бошад?
Дар PyTorch nn.CrossEntropyLoss талафотро бевосита аз логитҳо, яъне баҳои хоми синфҳо, ҳисоб мекунад, на аз эҳтимолиятҳо.
Ин аз баъзе ҳисобҳои гарон, аз ҷумла логарифмҳо ва экспонентаҳои ҳамдигарро бекоркунанда, канорагирӣ мекунад ва ҳисоббарорию хотираро сарфа менамояд. Ҳамчунин аз ҷиҳати ададӣ устувортар аст.
Аз ин рӯ, модел бояд логитҳо баргардонад. Ҳар гоҳ эҳтимолияти синфҳо лозим шавад, softmax-ро дастӣ татбиқ мекунем.
Функсияҳои дигари талафоти таснифбандӣ
Барои таснифбандии бисёрсинфӣ роҳи дигар илова кардани nn.LogSoftmax ба баромад ва истифодаи nn.NLLLoss — талафоти логарифми манфии ростнамоӣ — мебошад.
Модел он гоҳ логарифми эҳтимолиятҳоро бармегардонад. Барои гирифтани эҳтимолиятҳои воқеӣ экспонентаи онҳоро мегиред.
Ин равиш нисбат ба nn.CrossEntropyLoss андаке сусттар аст, аммо вақте лозим аст модел логарифми эҳтимолиятҳоро диҳад ё пеш аз ҳисоб кардани талафот тақсимотро тағйир диҳед, муфид буда метавонад.
Барои таснифбандии дусинфӣ дар қабати баромад як нейрон ва талафоти nn.BCEWithLogitsLoss истифода мешавад. BCE маънои энтропияи салибии дусинфиро дорад.
Модел логит медиҳад. Барои гирифтани эҳтимолияти синфи мусбат sigmoid-ро татбиқ кунед.
Роҳи дигар илова кардани nn.Sigmoid ба баромад ва истифодаи nn.BCELoss аст. Модел мустақиман эҳтимолият медиҳад, аммо ин равиш андаке сусттар ва камустувортар аст.
Барои таснифбандии дусинфии чандбарчаспа фарқ танҳо дар он аст, ки дар баромад барои ҳар барчасп як нейрон лозим мешавад.
Акнун моделро бо функсияи train() ва оптимизатори SGD меомӯзонем.
Барои арзёбӣ меъёри ҷараёнии Accuracy-ро сохта, ба GPU мебарем:
accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=10).to(device)
Огоҳӣ. Омӯзиш бо GPU чанд дақиқа ва бе он хеле бештар вақт мегирад. Кор бо тасвирҳо нисбат ба додаҳои камандоза ҳисоббарорӣ ва хотираи хеле бештар талаб мекунад.
Модел дар маҷмӯи омӯзишӣ тақрибан 92,8% ва дар маҷмӯи тасдиқӣ 87,2% саҳеҳӣ мегирад. Натиҷаҳо вобаста ба тезонандаи сахтафзорӣ андаке фарқ карда метавонанд.
Ин нишон медиҳад, ки каме аз ҳад мутобиқшавӣ вуҷуд дорад. Метавон шумораи нейронҳоро кам кард ё мунтазамсозӣ илова намуд. Ба боби 11 нигаред.
Барои се тасвири аввали бастаи тасдиқӣ пешгӯӣ мекунем:
>>> model.eval()
>>> X_new, y_new = next(iter(valid_loader))
>>> X_new = X_new[:3].to(device)
>>> with torch.no_grad():
... y_pred_logits = model(X_new)
...
>>> y_pred = y_pred_logits.argmax(dim=1) # индекси логити калонтарин
>>> y_pred
tensor([7, 4, 2], device='cuda:0')
>>> [train_and_valid_data.classes[index] for index in y_pred]
['Sneaker', 'Coat', 'Pullover']
Синфи пешгӯишуда барои ҳар тасвир синфи дорои логити калонтарин аст.
Дар ин мисол ҳар се пешгӯӣ дурустанд: кроссовка, палто ва пуловер.
Барои гирифтани эҳтимолиятҳо softmax-ро ба логитҳо татбиқ мекунем.
Метавонистем nn.Softmax созем, аммо функсияи softmax() аз torch.nn.functional, ки одатан ҳамчун F ворид мешавад, кофист:
>>> import torch.nn.functional as F
>>> y_proba = F.softmax(y_pred_logits, dim=1)
>>> y_proba.round(decimals=3)
tensor([[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0010, 0.0000, 0.9110, 0.0000,
0.0880],
[0.0000, 0.0000, 0.0040, 0.0000, 0.9960, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000],
[0.0000, 0.0000, 0.6250, 0.0000, 0.3350, 0.0000, 0.0390, 0.0000, 0.0000,
0.0000]], device='cuda:0')
Мисли боби 9, модел ба ду пешгӯии аввал хеле эътимод дорад: мутаносибан 91,1% ва 99,6%.
Маслиҳат. Барои истифодаи ҳамворсозии барчаспҳо — label smoothing, ба гиперпараметри
label_smoothing-иnn.CrossEntropyLossқимати дилхоҳи байни0ва1, масалан,0.05, диҳед.
Баъзан гирифтани k пешгӯии беҳтарин муфид аст.
Функсияи torch.topk() ҳам k қимати калонтарин ва ҳам индексҳои онҳоро бармегардонад:
>>> y_top4_probas, y_top4_indices = torch.topk(y_proba, k=4, dim=1)
>>> y_top4_probas.round(decimals=3)
tensor([[0.9110, 0.0880, 0.0010, 0.0000],
[0.9960, 0.0040, 0.0000, 0.0000],
[0.6250, 0.3350, 0.0390, 0.0000]], device='cuda:0')
>>> y_top4_indices
tensor([[7, 9, 5, 8],
[4, 2, 6, 0],
[2, 4, 6, 0]], device='cuda:0')
Эҳтимолиятҳоро аз y_proba, ки softmax-и ҳамаи 10 синф аст, мегирем. Агар softmax-ро баъди интихоби чор логит татбиқ кунем, эҳтимолиятҳо танҳо байни ҳамон чор синф тақсим шуда, аз ҳад калон мебароянд.
Барои тасвири якум тахмини беҳтарин синфи 7, яъне кроссовка, бо эътимоди 91,1% ва тахмини дуюм синфи 9, яъне мӯзаи кӯтоҳ, бо 8,8% мебошад.
Маслиҳат. Fashion MNIST мутавозин аст: ҳар синф шумораи якхелаи намунаҳо дорад. Барои маҷмӯи номутавозин одатан ба синфҳои камёб вазни бештар ва ба синфҳои маъмул вазни камтар диҳед. Дар акси ҳол модел ба синфҳои маъмул майл мекунад.
Барои ин аргументи
weight-иnn.CrossEntropyLoss-ро истифода баред. Масалан, агар се синф мутаносибан 900, 700 ва 400 намуна дошта бошанд, вазнҳо метавонанд2000/900,2000/700ва2000/400бошанд. Беҳтар аст онҳоро тавре нормалсозӣ кунед, ки ҷамъашон1шавад:weight = torch.tensor([0.2205, 0.2835, 0.4961])
Акнун сохтани шабакаҳои регрессия ва таснифбандӣ, омӯзиш ва арзёбии онҳоро медонед. Ба танзими гиперпараметрҳо мегузарем.
Танзими гиперпараметрҳои шабакаи нейронӣ бо Optuna
Дар боби 9 интихоби дастии қиматҳои муносибро баррасӣ кардем. Аммо агар хоҳед қиматҳои хубро ба таври худкор ҷустуҷӯ кунед, чӣ бояд кард?
Як роҳ табдил додани модели PyTorch ба баҳогузори Scikit-Learn мебошад. Барои ин синфи фармоишӣ менависед ё аз китобхонаи печонандае мисли Skorch истифода мебаред. Сипас бо GridSearchCV ё RandomizedSearchCV гиперпараметрҳоро танзим мекунед.
Аммо одатан китобхонаҳои махсуси зерин натиҷаи беҳтар медиҳанд:
Онҳо стратегияҳои пуриқтидори ҷустуҷӯ ва имкониятҳои зиёди танзим доранд.
Optuna-ро мебинем. Он дар Colab пешакӣ насб нашудааст:
%pip install optuna
Барои иҷрои маҳаллӣ ба дастури насб нигаред.
Суръати омӯзиш ва шумораи нейронҳои қабатҳои пинҳониро танзим мекунем. Барои содагӣ дар ҳар ду қабати пинҳонӣ шумораи якхела истифода мешавад.
Аввал функсияе месозем, ки Optuna онро чанд бор даъват мекунад. Он бояд:
- Объекти
Trialгирад. - Бо он қиматҳои гиперпараметрҳоро дархост кунад.
- Моделро бо ин қиматҳо сохта, омӯзонад.
- Одатан дар маҷмӯи тасдиқӣ арзёбӣ кунад.
- Қимати меъёрро баргардонад.
import optuna
def objective(trial):
learning_rate = trial.suggest_float("learning_rate", 1e-5, 1e-1, log=True)
n_hidden = trial.suggest_int("n_hidden", 20, 300)
model = ImageClassifier(n_inputs=1 * 28 * 28, n_hidden1=n_hidden,
n_hidden2=n_hidden, n_classes=10).to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
[...] # омӯзиш ва баъд арзёбӣ дар маҷмӯи тасдиқӣ
return validation_accuracy
Методҳои suggest_float() ва suggest_int() қиматҳои муносибро аз фосилаи муайян пешниҳод мекунанд. suggest_categorical() низ мавҷуд аст.
Барои суръати омӯзиш фосилаи аз 10⁻⁵ то 10⁻¹-ро мегирем.
Азбаски миқёси оптималиро намедонем, log=True мегузорем. Ин имкон медиҳад Optuna миқёсҳои гуногунро хубтар бисанҷад.
Бо тақсимоти яксони муқаррарӣ эҳтимоли санҷидани қиматҳои хеле хурд кам мебуд.
Барои оғози ҷустуҷӯ объекти Study месозем ва optimize()-ро бо функсияи ҳадаф ва шумораи озмоишҳо даъват мекунем.
Азбаски функсия саҳеҳиро медиҳад ва қимати баландтар беҳтар аст, direction="maximize" мегузорем. Ба таври пешфарз Optuna кӯшиш мекунад ҳадафро камтарин кунад.
Барои такрорпазирӣ тухми тасодуфии PyTorch ва намунагири Optuna-ро муайян мекунем:
torch.manual_seed(42)
sampler = optuna.samplers.TPESampler(seed=42)
study = optuna.create_study(direction="maximize", sampler=sampler)
study.optimize(objective, n_trials=5)
Ба таври пешфарз Optuna аз Tree-structured Parzen Estimator — TPE истифода мебарад.
Ин алгоритми беҳинасозии пайдарпайи ба модел асосёфта аст: аз натиҷаҳои гузашта меомӯзад, то қиматҳои умедбахши ояндаро беҳтар интихоб кунад.
Яъне Optuna аз қиматҳои тасодуфӣ оғоз карда, тадриҷан ҷустуҷӯро ба минтақаҳои умедбахши фазои гиперпараметрҳо равона мекунад.
Бо ҳамин вақт он метавонад нисбат ба ҷустуҷӯи комилан тасодуфӣ гиперпараметрҳои беҳтар ёбад.
Маслиҳат. Ба ҷустуҷӯ гиперпараметрҳои дигар, масалан, андозаи баста, навъи оптимизатор, шумораи қабатҳои пинҳонӣ ё функсияи фаъолсозиро низ илова кардан мумкин аст. Аммо бо зиёд шудани гиперпараметрҳо фазои ҷустуҷӯ босуръат калон мешавад. Санҷед, ки фоидаи эҳтимолӣ ба вақт ва ҳисоббарории иловагӣ меарзад.
Пас аз анҷом беҳтарин қиматҳо ва саҳеҳии мувофиқро мебинем:
>>> study.best_params
{'learning_rate': 0.008471801418819975, 'n_hidden': 188}
>>> study.best_value
0.8547999858856201
Ин аз натиҷаи пешина (87,2%) пасттар аст: ҳамагӣ 5 озмоиш, ҳар кадом 10 давр, гузаронидем.
Эзоҳи тарҷумон. Дар матни аслӣ ин ҷо
{'learning_rate': 0.0853, 'n_hidden': 116}ва0.8868оварда шуда, натиҷа «андаке беҳтар» номида мешавад. Ин қиматҳо дар дафтари боби 10 аз ҷустуҷӯи 20-озмоишаи боMedianPruner(дар поён) ҳосил шудаанд. Барои ҳамин 5 озмоиш дафтар қиматҳои болоро медиҳад.
Агар n_trials-ро то 50 ё бештар зиёд кунед, метавонед натиҷаи хеле беҳтар гиред, аммо иҷрои он соатҳо вақт мегирад.
Ҳамчунин метавонед optimize()-ро чанд бор даъват карда, вақте натиҷа қонеъкунанда шуд, қатъ намоед.
Optuna озмоишҳоро дар чанд компютер мувозӣ иҷро карда метавонад. Ин метавонад суръатро қариб мутаносиб ба шумораи мошинҳо зиёд кунад.
Барои ин пойгоҳи SQL, масалан, SQLite ё PostgreSQL, омода карда, storage-и create_study()-ро ба он равона мекунед.
Ҳамчунин study_name ва load_if_exists=True-ро муайян намоед.
Баъд скриптро дар чанд мошин иҷро мекунед. Агар тухмҳои тасодуфӣ истифода шаванд, онҳоро барои ҳар мошин гуногун гузоред.
Скриптҳо натиҷаҳои озмоишҳоро аз пойгоҳ хонда, ба он менависанд. Бартарии иловагӣ нигоҳ доштани таърихи пурраи таҷрибаҳост.
Дар мисоли мо objective() ба додаҳо эҳтимол тавассути тағйирёбандаҳои глобалӣ дастрасӣ дошт. Одатан беҳтар аст онҳоро ҳамчун аргументҳои иловагӣ диҳем:
def objective(trial, train_loader, valid_loader):
[...] # қисми боқимонда мисли пештара
objective_with_data = lambda trial: objective(
trial, train_loader=train_loader, valid_loader=valid_loader)
study.optimize(objective_with_data, n_trials=5)
Барои муқаррар кардани аргументҳои иловагӣ функсияи lambda сохтем.
Роҳи дигар functools.partial() аст, ки функсияи печонандаи сабук бо аргументҳои пешакӣ муайяншуда месозад:
from functools import partial
objective_with_data = partial(objective, train_loader=train_loader,
valid_loader=valid_loader)
Аксаран зуд фаҳмидан мумкин аст, ки озмоиш натиҷаи хеле бад медиҳад: масалан, талафот дар даври аввал якбора баланд мешавад ё модел дар чанд даври аввал қариб беҳтар намегардад.
Дар чунин ҳолат беҳтар аст омӯзишро барвақт қатъ карда, вақт ва ҳисоббарориро сарфа кунед.
Метавон саҳеҳии ҷории тасдиқиро баргардонд ва умед баст, ки Optuna аз он меомӯзад.
Роҳи дигар баровардани истиснои optuna.TrialPruned аст. Он ба Optuna мегӯяд, ки ин озмоиш бурида шудааст.
Дар бисёр ҳолатҳо ин ҷустуҷӯро самараноктар мекунад, зеро алгоритм бо арзёбиҳои пурғавғои зиёди моделҳо пур намешавад.
Optuna синфҳои гуногуни Pruner барои ошкор ва қатъ кардани озмоишҳои бад дорад.
Масалан, MedianPruner дар фосилаҳои муайян озмоишҳоеро қатъ мекунад, ки натиҷаашон аз медиана пасттар аст.
Он пас аз анҷоми шумораи муайяни озмоишҳо ба кор медарояд. Инро n_startup_trials идора мекунад, ки пешфарз 5 аст.
Барои ҳар озмоиши баъдӣ аввал чанд қадами гармшавӣ иҷозат дода мешавад: n_warmup_steps, пешфарз 0.
Баъд ҳар чанд давр, мувофиқи interval_steps, натиҷа бо медианаи ҳамон даври озмоишҳои гузашта муқоиса мешавад.
pruner = optuna.pruners.MedianPruner(n_startup_trials=5, n_warmup_steps=0,
interval_steps=1)
study = optuna.create_study(direction="maximize", sampler=sampler,
pruner=pruner)
Дар objective() пас аз ҳар давр чунин код илова мекунем:
for epoch in range(n_epochs):
[...] # омӯзиш барои як давр
validation_accuracy = [...] # арзёбии саҳеҳӣ
trial.report(validation_accuracy, epoch)
if trial.should_prune():
raise optuna.TrialPruned()
report() ба Optuna саҳеҳӣ ва рақами даврро хабар медиҳад.
Агар should_prune() қимати True диҳад, истиснои TrialPruned мебарорем.
Маслиҳат. Optuna имкониятҳои дигари зиёде дорад: воситаҳои тасвирсозӣ, нигоҳдории натиҷаҳои озмоиш ва артефактҳо, панели идоракунӣ барои беҳинасозӣ бо иштироки инсон ва алгоритмҳои дигари ҷустуҷӯ ва қатъи озмоишҳо.
Пас аз интихоби гиперпараметрҳои қонеъкунанда, моделро дар маҷмӯи пурраи омӯзишӣ, яъне омӯзишӣ ва тасдиқии якҷошуда, омӯзонед. Баъд дар маҷмӯи санҷишӣ арзёбӣ кунед.
Агар натиҷа хуб бошад, моделро нигоҳ дошта, баъд барои истифода дар муҳити корӣ бор мекунед.
Нигоҳдорӣ ва боркунии моделҳои PyTorch
Роҳи содатарини нигоҳдорӣ torch.save() мебошад. Ба он модел ва роҳ ба файлро медиҳед.
Объекти модел бо pickle-и Python ба пайдарпайии байтҳо табдил меёбад, сипас дар формати zip нигоҳ дошта мешавад.
Одатан барои файлҳои PyTorch пасвандҳои .pt ё .pth истифода мешаванд:
torch.save(model, "my_fashion_mnist.pt")
Боркунии модел низ осон аст:
loaded_model = torch.load("my_fashion_mnist.pt", weights_only=False)
Огоҳӣ. Агар модел функсия ё синфи фармоишӣ, масалан,
ImageClassifier, истифода барад,torch.save()танҳо истинод ба онҳоро нигоҳ медорад, на худи кодро. Пеш азtorch.load()бояд коди фармоишӣ дар муҳити Python дастрас бошад. Барои пешгирии номувофиқатӣ ҳамон версияи кодро истифода баред.
weights_only=False имкон медиҳад тамоми объекти модел бор шавад, на танҳо параметрҳо.
Пеш аз пешгӯӣ ҳолати арзёбиро фаъол кунед:
loaded_model.eval()
y_pred_logits = loaded_model(X_new)
Ин сода аст, аммо ду камбудии ҷиддӣ дорад.
Якум, амнияти pickle. Формати pickle метавонад коди иҷрошавандаро дар бар гирад. Шахси бадният метавонад дар модели нигоҳдошташуда коди зараровар ҷой диҳад, ки ҳангоми боркунӣ иҷро мешавад. Аз ин рӯ, моделро бо ин роҳ танҳо аз манбаи комилан боэътимод бор кунед.
Дуюм, нозукии вобастагиҳо. Формати pickle метавонад аз версияи Python вобаста бошад ва роҳҳои мушаххаси файлҳоро барои ёфтани код нигоҳ дорад. Агар сохтори ҷузвдонҳо дар муҳити боркунӣ дигар бошад, мушкил пайдо мешавад.
Барои пешгирӣ, одатан танҳо вазнҳо ва ҳолати модел нигоҳ дошта мешаванд:
torch.save(model.state_dict(), "my_fashion_mnist_weights.pt")
state_dict() луғати тартибдори Python — OrderedDict — бармегардонад, ки параметрҳои моделро нигоҳ медорад.
Он ҳамчунин буферҳо-ро, агар мавҷуд бошанд, дар бар мегирад.
Буфер тензори муқаррарист, ки бо register_buffer() дар модел ё зермодули он ба қайд гирифта шудааст. Он маълумоти иловагии ҳамроҳи модел нигоҳдошташавандаро дар бар мегирад, аммо параметри омӯхташаванда нест.
Дар боби 11 мисоли онро дар қабати batch normalization мебинем.
Барои бор кардани вазнҳо аввал модели дорои айнан ҳамон сохтор месозем, баъд вазнҳоро бо weights_only=True мехонем ва ба load_state_dict() медиҳем:
new_model = ImageClassifier(n_inputs=1 * 28 * 28, n_hidden1=300, n_hidden2=100,
n_classes=10)
loaded_weights = torch.load("my_fashion_mnist_weights.pt", weights_only=True)
new_model.load_state_dict(loaded_weights)
new_model.eval()
Дар ин равиш файл барои нигоҳ доштани додаҳои модел истифода мешавад ва эҳтимоли мушкилоти вобаста ба версияҳои Python ё сохтори муҳит камтар аст.
Аммо бояд пеш аз боркунӣ айнан ҳамон меъмориро сохта тавонед. Барои ин шумораи қабатҳо, нейронҳои ҳар қабат ва дигар параметрҳои сохториро бояд донед.
Беҳтар аст ин маълумотро ҳамроҳи state_dict нигоҳ доред:
model_data = {
"model_state_dict": model.state_dict(),
"model_hyperparameters": {"n_inputs": 1 * 28 * 28, "n_hidden1": 300, [...]}
}
torch.save(model_data, "my_fashion_mnist_model.pt")
Баъд луғатро бор карда, аз рӯйи гиперпараметрҳо модел месозед ва ҳолатро ба он медиҳед:
loaded_data = torch.load("my_fashion_mnist_model.pt", weights_only=True)
new_model = ImageClassifier(**loaded_data["model_hyperparameters"])
new_model.load_state_dict(loaded_data["model_state_dict"])
new_model.eval()
Агар хоҳед омӯзишро аз ҷойи қатъшуда идома диҳед, ҳолати оптимизатор, гиперпараметрҳои он ва маълумоти дигари зарурӣ, масалан, рақами даври ҷорӣ ва таърихи талафотро низ нигоҳ доред.
Маслиҳат. Китобхонаи safetensors-и Hugging Face роҳи дигари маъмули нигоҳдории бехатари вазнҳои модел аст.
Роҳи дигари нигоҳдорӣ ва боркунӣ табдил додани модел ба TorchScript мебошад. Ин ҳамчунин метавонад пешгӯиро тезонад.
Компилятсия ва беҳинасозии модели PyTorch
PyTorch метавонад коди моделро ба TorchScript табдил диҳад. Онро метавон зермаҷмӯи дорои навъҳои статикии Python тасаввур кард.
Ду бартарии асосӣ дорад:
- Кодро компилятсия ва беҳина кардан мумкин аст. Масалан, чанд амал метавонад ба як амали самараноктар муттаҳид шавад. Амалҳои собит, масалан,
2 * 3, бо натиҷаи6иваз мешаванд — ин constant folding ном дорад. Коди истифоданашаванда хориҷ мешавад ва ғайра. - TorchScript-ро метавон нигоҳ дошт ва баъд дар Python ё муҳити C++ бо LibTorch бор карда, иҷро намуд. Ин истифодаи моделҳоро дар дастгоҳҳои гуногун, аз ҷумла дастгоҳҳои дарунсохт, имконпазир мекунад.
Ду роҳи асосии табдил ба TorchScript вуҷуд дорад.
Tracing — пайгирии иҷро
PyTorch моделро бо додаҳои намунавӣ иҷро карда, ҳамаи амалҳоро сабт мекунад ва ин сабтро ба TorchScript табдил медиҳад:
torchscript_model = torch.jit.trace(model, X_new)
Ин одатан барои моделҳои статикӣ хуб кор мекунад, ки forward()-и онҳо шарт ё ҳалқаҳои вобаста ба додаҳо надорад.
Агар модел if ё match дошта бошад, танҳо шохаи воқеан иҷрошуда сабт мешавад. Ин аксаран он чизе нест, ки мехоҳед.
Агар ҳалқа бошад, коди TorchScript барои ҳар такрори воқеан иҷрошуда як нусхаи амалҳои дохили ҳалқаро дар бар мегирад.
Scripting — таҳлили мустақими код
Барои моделҳои динамикӣ scripting мувофиқтар буда метавонад.
Дар ин ҳолат PyTorch худи коди Python-ро таҳлил карда, ба TorchScript табдил медиҳад.
Он шартҳои if ва ҳалқаҳои while-ро дуруст дастгирӣ мекунад, агар шартҳо мувофиқи талаботи он ифода шуда бошанд. Гузариш бо for аз болои тензорҳо низ дастгирӣ мешавад.
Аммо танҳо зермаҷмӯи Python дастгирӣ мешавад. Масалан, маҳдудиятҳо ба чунин имкониятҳо дахл доранд:
- тағйирёбандаҳои глобалӣ;
- генераторҳо ва
yield; - ифодаҳои мураккаби сохтани рӯйхат;
- аргументҳои дарозии тағйирёбанда —
*argsва**kwargs; - оператори
match.
Навъҳо бояд собит бошанд: функсия наметавонад дар як ҳолат адади бутун ва дар ҳолати дигар адади касрӣ баргардонад.
Функсияҳои даъватшаванда низ бояд ба қоидаҳои TorchScript мувофиқ бошанд. Рӯйхати пурраи маҳдудиятҳо дар ҳуҷҷатгузорӣ оварда шудааст.
Ин қоидаҳо мураккаб менамоянд, аммо барои бисёр моделҳои амалӣ риояашон чандон душвор нест:
torchscript_model = torch.jit.script(model)
Пас аз tracing ё scripting моделро боз беҳина кардан мумкин аст:
optimized_model = torch.jit.optimize_for_inference(torchscript_model)
Модели беҳинашударо мисли модели муқаррарии PyTorch барои пешгӯӣ даъват мекунем:
y_pred_logits = optimized_model(X_new)
Модели TorchScript-ро бо save() нигоҳ медорем:
torchscript_model.save('my_fashion_mnist_torchscript.pt')
Ва бо torch.jit.load() бор мекунем:
loaded_torchscript_model = torch.jit.load("my_fashion_mnist_torchscript.pt")
TorchScript дигар фаъолона имкониятҳои нав намегирад: хатоҳо ислоҳ мешаванд, аммо таҳияи хусусиятҳои нав маҳдуд шудааст.
Эзоҳи тарҷумон. Дар PyTorch 2.14
torch.jit.trace(),torch.jit.script(),torch.jit.optimize_for_inference()ваtorch.jit.load()ҳанӯз кор мекунанд, аммоFutureWarningмедиҳанд: онҳо куҳнашуда эълон шудаанд ва ба ҷояшонtorch.compile()ёtorch.exportтавсия мешавад.
Он ҳанӯз барои иҷрои моделҳои PyTorch дар муҳити C++ истифода мешавад. Аммо аз нашри PyTorch 2.0 дар моҳи марти соли 2023 даста ба маҷмӯи нави воситаҳои компилятсия дар атрофи torch.compile() диққат медиҳад:
compiled_model = torch.compile(model)
Модели ҳосилшударо ба таври муқаррарӣ истифода мебаред. Ҳангоми истифода он ба таври худкор компилятсия ва беҳина мешавад.
Ин компилятсия ҳангоми иҷро — JIT (Just-In-Time) ном дорад, бар хилофи компилятсияи пешакӣ — AOT (Ahead-Of-Time).
Дар дохил torch.compile() ба TorchDynamo, ё кӯтоҳ Dynamo, такя мекунад. Он ба байткоди Python пайваст шуда, графи ҳисоббарории моделро ҳангоми иҷро ба даст меорад.
Дастрасӣ ба байткод имкон медиҳад граф самаранок ва боэътимод гирифта шавад, шартҳо ва ҳалқаҳо дуруст коркард шаванд ва маълумоти динамикӣ барои беҳинасозӣ истифода гардад.
Компилятсия ва беҳинасозии воқеӣ ба таври пешфарз аз ҷониби TorchInductor иҷро мешаванд.
Он барои тавлиди коди самараноки GPU аз забони Triton ва барои беҳинасозии CPU аз OpenMP истифода мебарад.
PyTorch 2.x пуштибонҳои дигари беҳинасозӣ, аз ҷумла XLA барои TPU-ҳои Google, низ дорад.
Акнун барои оғоз кардани сохтани шабакаҳои нейронии мураккаб ва самаранок воситаҳои асосиро медонед.
Дар ин боб мавзуъҳои зиёдеро фаро гирифтем, аммо омӯзиш нав оғоз ёфтааст. Дар боби оянда усулҳои омӯзонидани шабакаҳои хеле амиқро баррасӣ мекунем.
Баъд ба меъмориҳои дигари маъмул мегузарем:
- шабакаҳои нейронии конволютсионӣ барои коркарди тасвир;
- шабакаҳои бозгаштӣ барои додаҳои пайдарпай;
- трансформерҳо барои матн ва вазифаҳои дигар;
- автоэнкодерҳо барои омӯзиши намоиши додаҳо;
- шабакаҳои тавлидии рақобатӣ ва моделҳои диффузионӣ барои тавлиди додаҳо.
Сипас омӯзиши тақвиятиро барои омӯзонидани агентҳои худмухтор мебинем ва дар охир ҷойгиркунӣ ва беҳинасозии моделҳои PyTorch-ро муфассалтар меомӯзем.

Барномасозӣ
Олами Афсонаҳо
Ахбор