411 lines
21 KiB
Plaintext
411 lines
21 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Generativní sítě\n",
|
|
"\n",
|
|
"Rekurentní neuronové sítě (RNN) a jejich varianty s bráněnými buňkami, jako jsou Long Short Term Memory Cells (LSTMs) a Gated Recurrent Units (GRUs), poskytly mechanismus pro modelování jazyka, tj. dokážou se naučit pořadí slov a poskytovat predikce pro další slovo v sekvenci. To nám umožňuje používat RNN pro **generativní úlohy**, jako je běžná generace textu, strojový překlad a dokonce i popisování obrázků.\n",
|
|
"\n",
|
|
"V architektuře RNN, kterou jsme probírali v předchozí jednotce, každá jednotka RNN produkovala jako výstup další skrytý stav. Můžeme však také přidat další výstup ke každé rekurentní jednotce, což by nám umožnilo generovat **sekvenci** (která je stejně dlouhá jako původní sekvence). Navíc můžeme použít RNN jednotky, které nepřijímají vstup na každém kroku, ale pouze vezmou nějaký počáteční stavový vektor a poté produkují sekvenci výstupů.\n",
|
|
"\n",
|
|
"V tomto notebooku se zaměříme na jednoduché generativní modely, které nám pomohou generovat text. Pro zjednodušení vytvoříme **síť na úrovni znaků**, která generuje text písmeno po písmenu. Během trénování potřebujeme vzít nějaký textový korpus a rozdělit ho na sekvence písmen.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Loading dataset...\n",
|
|
"Building vocab...\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import torch\n",
|
|
"import torchtext\n",
|
|
"import numpy as np\n",
|
|
"from torchnlp import *\n",
|
|
"train_dataset,test_dataset,classes,vocab = load_dataset()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Vytváření slovníku znaků\n",
|
|
"\n",
|
|
"Pro vytvoření generativní sítě na úrovni znaků je potřeba text rozdělit na jednotlivé znaky namísto slov. Toho lze dosáhnout definováním odlišného tokenizéru:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Vocabulary size = 82\n",
|
|
"Encoding of 'a' is 1\n",
|
|
"Character with code 13 is c\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def char_tokenizer(words):\n",
|
|
" return list(words) #[word for word in words]\n",
|
|
"\n",
|
|
"counter = collections.Counter()\n",
|
|
"for (label, line) in train_dataset:\n",
|
|
" counter.update(char_tokenizer(line))\n",
|
|
"vocab = torchtext.vocab.vocab(counter)\n",
|
|
"\n",
|
|
"vocab_size = len(vocab)\n",
|
|
"print(f\"Vocabulary size = {vocab_size}\")\n",
|
|
"print(f\"Encoding of 'a' is {vocab.get_stoi()['a']}\")\n",
|
|
"print(f\"Character with code 13 is {vocab.get_itos()[13]}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Podívejme se na příklad, jak můžeme zakódovat text z našeho datového souboru:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"tensor([ 0, 1, 2, 2, 3, 4, 5, 6, 3, 7, 8, 1, 9, 10, 3, 11, 2, 1,\n",
|
|
" 12, 3, 7, 1, 13, 14, 3, 15, 16, 5, 17, 3, 5, 18, 8, 3, 7, 2,\n",
|
|
" 1, 13, 14, 3, 19, 20, 8, 21, 5, 8, 9, 10, 22, 3, 20, 8, 21, 5,\n",
|
|
" 8, 9, 10, 3, 23, 3, 4, 18, 17, 9, 5, 23, 10, 8, 2, 2, 8, 9,\n",
|
|
" 10, 24, 3, 0, 1, 2, 2, 3, 4, 5, 9, 8, 8, 5, 25, 10, 3, 26,\n",
|
|
" 12, 27, 16, 26, 2, 27, 16, 28, 29, 30, 1, 16, 26, 3, 17, 31, 3, 21,\n",
|
|
" 2, 5, 9, 1, 23, 13, 32, 16, 27, 13, 10, 24, 3, 1, 9, 8, 3, 10,\n",
|
|
" 8, 8, 27, 16, 28, 3, 28, 9, 8, 8, 16, 3, 1, 28, 1, 27, 16, 6])"
|
|
]
|
|
},
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def enc(x):\n",
|
|
" return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n",
|
|
"\n",
|
|
"enc(train_dataset[0][1])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Trénování generativní RNN\n",
|
|
"\n",
|
|
"Způsob, jakým budeme trénovat RNN pro generování textu, je následující. Při každém kroku vezmeme sekvenci znaků o délce `nchars` a požádáme síť, aby pro každý vstupní znak vygenerovala následující výstupní znak:\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"V závislosti na konkrétním scénáři můžeme také chtít zahrnout některé speciální znaky, jako například *konec sekvence* `<eos>`. V našem případě chceme síť trénovat pouze pro nekonečné generování textu, a proto nastavíme velikost každé sekvence na `nchars` tokenů. Každý tréninkový příklad tedy bude sestávat z `nchars` vstupů a `nchars` výstupů (což je vstupní sekvence posunutá o jeden symbol doleva). Minibatch bude obsahovat několik takových sekvencí.\n",
|
|
"\n",
|
|
"Způsob, jakým budeme generovat minibatche, spočívá v tom, že vezmeme každý text zprávy o délce `l` a vytvoříme z něj všechny možné kombinace vstup-výstup (těchto kombinací bude `l-nchars`). Tyto kombinace vytvoří jeden minibatch a velikost minibatchů se bude při každém tréninkovém kroku lišit.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"(tensor([[ 0, 1, 2, ..., 28, 29, 30],\n",
|
|
" [ 1, 2, 2, ..., 29, 30, 1],\n",
|
|
" [ 2, 2, 3, ..., 30, 1, 16],\n",
|
|
" ...,\n",
|
|
" [20, 8, 21, ..., 1, 28, 1],\n",
|
|
" [ 8, 21, 5, ..., 28, 1, 27],\n",
|
|
" [21, 5, 8, ..., 1, 27, 16]]),\n",
|
|
" tensor([[ 1, 2, 2, ..., 29, 30, 1],\n",
|
|
" [ 2, 2, 3, ..., 30, 1, 16],\n",
|
|
" [ 2, 3, 4, ..., 1, 16, 26],\n",
|
|
" ...,\n",
|
|
" [ 8, 21, 5, ..., 28, 1, 27],\n",
|
|
" [21, 5, 8, ..., 1, 27, 16],\n",
|
|
" [ 5, 8, 9, ..., 27, 16, 6]]))"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"nchars = 100\n",
|
|
"\n",
|
|
"def get_batch(s,nchars=nchars):\n",
|
|
" ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
|
|
" outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
|
|
" for i in range(len(s)-nchars):\n",
|
|
" ins[i] = enc(s[i:i+nchars])\n",
|
|
" outs[i] = enc(s[i+1:i+nchars+1])\n",
|
|
" return ins,outs\n",
|
|
"\n",
|
|
"get_batch(train_dataset[0][1])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Nyní si definujeme generátorovou síť. Může být založena na jakékoli rekurentní buňce, o které jsme diskutovali v předchozí kapitole (jednoduchá, LSTM nebo GRU). V našem příkladu použijeme LSTM.\n",
|
|
"\n",
|
|
"Protože síť přijímá znaky jako vstup a velikost slovníku je poměrně malá, nepotřebujeme vrstvu pro vkládání (embedding layer), jednohotově zakódovaný vstup může přímo přejít do LSTM buňky. Nicméně, protože předáváme čísla znaků jako vstup, musíme je před předáním do LSTM jednohotově zakódovat. To se provádí voláním funkce `one_hot` během průchodu `forward`. Výstupní enkodér bude lineární vrstva, která převede skrytý stav na jednohotově zakódovaný výstup.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class LSTMGenerator(torch.nn.Module):\n",
|
|
" def __init__(self, vocab_size, hidden_dim):\n",
|
|
" super().__init__()\n",
|
|
" self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n",
|
|
" self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n",
|
|
"\n",
|
|
" def forward(self, x, s=None):\n",
|
|
" x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n",
|
|
" x,s = self.rnn(x,s)\n",
|
|
" return self.fc(x),s"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Během trénování chceme mít možnost vzorkovat generovaný text. Abychom toho dosáhli, definujeme funkci `generate`, která vytvoří výstupní řetězec délky `size`, začínající počátečním řetězcem `start`.\n",
|
|
"\n",
|
|
"Funguje to následujícím způsobem. Nejprve projdeme celý počáteční řetězec sítí a získáme výstupní stav `s` a další předpovězený znak `out`. Protože `out` je zakódován jako one-hot, použijeme `argmax` k získání indexu znaku `nc` ve slovníku a pomocí `itos` zjistíme skutečný znak, který přidáme do výsledného seznamu znaků `chars`. Tento proces generování jednoho znaku opakujeme `size` krát, abychom vygenerovali požadovaný počet znaků.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def generate(net,size=100,start='today '):\n",
|
|
" chars = list(start)\n",
|
|
" out, s = net(enc(chars).view(1,-1).to(device))\n",
|
|
" for i in range(size):\n",
|
|
" nc = torch.argmax(out[0][-1])\n",
|
|
" chars.append(vocab.get_itos()[nc])\n",
|
|
" out, s = net(nc.view(1,-1),s)\n",
|
|
" return ''.join(chars)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Teď se pustíme do tréninku! Trénovací smyčka je téměř stejná jako ve všech našich předchozích příkladech, ale místo přesnosti tiskneme každých 1000 epoch vygenerovaný text.\n",
|
|
"\n",
|
|
"Zvláštní pozornost je třeba věnovat způsobu, jakým počítáme ztrátu. Musíme vypočítat ztrátu na základě jednohotově zakódovaného výstupu `out` a očekávaného textu `text_out`, což je seznam indexů znaků. Naštěstí funkce `cross_entropy` očekává jako první argument nenormalizovaný výstup sítě a jako druhý číslo třídy, což je přesně to, co máme. Navíc automaticky provádí průměrování přes velikost minibatch.\n",
|
|
"\n",
|
|
"Trénink také omezujeme na počet vzorků `samples_to_train`, abychom nemuseli čekat příliš dlouho. Doporučujeme experimentovat a zkusit delší trénink, případně několik epoch (v takovém případě byste museli vytvořit další smyčku kolem tohoto kódu).\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Current loss = 4.398899078369141\n",
|
|
"today sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr s\n",
|
|
"Current loss = 2.161320447921753\n",
|
|
"today and to the tor to to the tor to to the tor to to the tor to to the tor to to the tor to to the tor t\n",
|
|
"Current loss = 1.6722588539123535\n",
|
|
"today and the court to the could to the could to the could to the could to the could to the could to the c\n",
|
|
"Current loss = 2.423795223236084\n",
|
|
"today and a second to the conternation of the conternation of the conternation of the conternation of the \n",
|
|
"Current loss = 1.702607274055481\n",
|
|
"today and the company to the company to the company to the company to the company to the company to the co\n",
|
|
"Current loss = 1.692358136177063\n",
|
|
"today and the company to the company to the company to the company to the company to the company to the co\n",
|
|
"Current loss = 1.9722288846969604\n",
|
|
"today and the control the control the control the control the control the control the control the control \n",
|
|
"Current loss = 1.8705692291259766\n",
|
|
"today and the second to the second to the second to the second to the second to the second to the second t\n",
|
|
"Current loss = 1.7626899480819702\n",
|
|
"today and a security and a security and a security and a security and a security and a security and a secu\n",
|
|
"Current loss = 1.5574463605880737\n",
|
|
"today and the company and the company and the company and the company and the company and the company and \n",
|
|
"Current loss = 1.5620026588439941\n",
|
|
"today and the be that the be the be that the be the be that the be the be that the be the be that the be t\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"net = LSTMGenerator(vocab_size,64).to(device)\n",
|
|
"\n",
|
|
"samples_to_train = 10000\n",
|
|
"optimizer = torch.optim.Adam(net.parameters(),0.01)\n",
|
|
"loss_fn = torch.nn.CrossEntropyLoss()\n",
|
|
"net.train()\n",
|
|
"for i,x in enumerate(train_dataset):\n",
|
|
" # x[0] is class label, x[1] is text\n",
|
|
" if len(x[1])-nchars<10:\n",
|
|
" continue\n",
|
|
" samples_to_train-=1\n",
|
|
" if not samples_to_train: break\n",
|
|
" text_in, text_out = get_batch(x[1])\n",
|
|
" optimizer.zero_grad()\n",
|
|
" out,s = net(text_in)\n",
|
|
" loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n",
|
|
" loss.backward()\n",
|
|
" optimizer.step()\n",
|
|
" if i%1000==0:\n",
|
|
" print(f\"Current loss = {loss.item()}\")\n",
|
|
" print(generate(net))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Tento příklad již generuje poměrně dobrý text, ale lze jej dále vylepšit několika způsoby:\n",
|
|
"* **Lepší generování minibatchů**. Způsob, jakým jsme připravili data pro trénink, byl generování jednoho minibatche z jednoho vzorku. To není ideální, protože minibatche mají různé velikosti a některé z nich nelze ani vygenerovat, protože text je kratší než `nchars`. Navíc malé minibatche nedostatečně využívají GPU. Bylo by rozumnější vzít jeden velký blok textu ze všech vzorků, poté vygenerovat všechny páry vstup-výstup, zamíchat je a vytvořit minibatche stejné velikosti.\n",
|
|
"* **Vícevrstvé LSTM**. Má smysl vyzkoušet 2 nebo 3 vrstvy LSTM buněk. Jak jsme zmínili v předchozí jednotce, každá vrstva LSTM extrahuje určité vzory z textu, a v případě generátoru na úrovni znaků můžeme očekávat, že nižší úroveň LSTM bude zodpovědná za extrakci slabik, zatímco vyšší úrovně - za slova a kombinace slov. To lze jednoduše implementovat předáním parametru počtu vrstev konstruktoru LSTM.\n",
|
|
"* Můžete také experimentovat s **GRU jednotkami** a zjistit, které z nich podávají lepší výkon, a s **různými velikostmi skrytých vrstev**. Příliš velká skrytá vrstva může vést k přeučení (např. síť se naučí přesný text), zatímco menší velikost nemusí produkovat dobré výsledky.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Generování měkkého textu a teplota\n",
|
|
"\n",
|
|
"V předchozí definici `generate` jsme vždy vybírali znak s nejvyšší pravděpodobností jako další znak v generovaném textu. To často vedlo k tomu, že se text \"cyklil\" mezi stejnými sekvencemi znaků znovu a znovu, jako v tomto příkladu:\n",
|
|
"```\n",
|
|
"today of the second the company and a second the company ...\n",
|
|
"```\n",
|
|
"\n",
|
|
"Pokud se však podíváme na rozložení pravděpodobností pro další znak, může se stát, že rozdíl mezi několika nejvyššími pravděpodobnostmi není velký, např. jeden znak může mít pravděpodobnost 0.2, jiný 0.19, atd. Například při hledání dalšího znaku v sekvenci '*play*' může být další znak stejně dobře mezera nebo **e** (jako ve slově *player*).\n",
|
|
"\n",
|
|
"To nás vede k závěru, že není vždy \"spravedlivé\" vybírat znak s vyšší pravděpodobností, protože výběr druhého nejvyššího může stále vést k smysluplnému textu. Je rozumnější **vzorkovat** znaky z rozložení pravděpodobností, které poskytuje výstup sítě.\n",
|
|
"\n",
|
|
"Toto vzorkování lze provést pomocí funkce `multinomial`, která implementuje tzv. **multinomické rozložení**. Funkce, která implementuje toto **měkké** generování textu, je definována níže:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"--- Temperature = 0.3\n",
|
|
"Today and a company and complete an all the land the restrational the as a security and has provers the pay to and a report and the computer in the stand has filities and working the law the stations for a company and with the company and the final the first company and refight of the state and and workin\n",
|
|
"\n",
|
|
"--- Temperature = 0.8\n",
|
|
"Today he oniis its first to Aus bomblaties the marmation a to manan boogot that pirate assaid a relaid their that goverfin the the Cappets Ecrotional Assonia Cition targets it annight the w scyments Blamity #39;s TVeer Diercheg Reserals fran envyuil that of ster said access what succers of Dour-provelith\n",
|
|
"\n",
|
|
"--- Temperature = 1.0\n",
|
|
"Today holy they a 11 will meda a toket subsuaties, engins for Chanos, they's has stainger past to opening orital his thempting new Nattona was al innerforder advan-than #36;s night year his religuled talitatian what the but with Wednesday to Justment will wemen of Mark CCC Camp as Timed Nae wome a leaders\n",
|
|
"\n",
|
|
"--- Temperature = 1.3\n",
|
|
"Today gpone 2.5 fech atcusion poor cocles toparsdorM.cht Line Pamage put 43 his calt lowed to the book, that has authh-the silia rruch ailing to'ory andhes beutirsimi- Aefffive heading offil an auf eacklets is charged evis, Gunymy oy) Mony has it after-sloythyor loveId out filme, the Natabl -Najuntaxiggs \n",
|
|
"\n",
|
|
"--- Temperature = 1.8\n",
|
|
"Today plary, P.slan chly\\401 mardregationly #39;t 8.1Mide) closes ,filtcon alfly playin roven!\\grea.-QFBEP: Iss onfarchQ/itilia CCf Zivesigntwasta orce.-Peul-aw.uicrin of fuglinfsut aftaningwo, MIEX awayew Aice Woiduar Corvagiugge oppo esig ThusBratourid canthly-RyI.co lagitems\\eexciaishes.conBabntusmor I\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def generate_soft(net,size=100,start='today ',temperature=1.0):\n",
|
|
" chars = list(start)\n",
|
|
" out, s = net(enc(chars).view(1,-1).to(device))\n",
|
|
" for i in range(size):\n",
|
|
" #nc = torch.argmax(out[0][-1])\n",
|
|
" out_dist = out[0][-1].div(temperature).exp()\n",
|
|
" nc = torch.multinomial(out_dist,1)[0]\n",
|
|
" chars.append(vocab.get_itos()[nc])\n",
|
|
" out, s = net(nc.view(1,-1),s)\n",
|
|
" return ''.join(chars)\n",
|
|
" \n",
|
|
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
|
|
" print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Zavedli jsme další parametr nazvaný **teplota**, který slouží k určení, jak pevně bychom se měli držet nejvyšší pravděpodobnosti. Pokud je teplota 1,0, provádíme spravedlivé multinomiální vzorkování, a když teplota dosáhne nekonečna - všechny pravděpodobnosti se stanou stejnými a náhodně vybíráme další znak. Na níže uvedeném příkladu můžeme pozorovat, že text se stává nesmyslným, když příliš zvýšíme teplotu, a připomíná „cyklický“ tvrdě generovaný text, když se blíží k 0.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Prohlášení**: \nTento dokument byl přeložen pomocí služby pro automatický překlad [Co-op Translator](https://github.com/Azure/co-op-translator). I když se snažíme o co největší přesnost, mějte prosím na paměti, že automatické překlady mohou obsahovat chyby nebo nepřesnosti. Za autoritativní zdroj by měl být považován původní dokument v jeho původním jazyce. Pro důležité informace doporučujeme profesionální lidský překlad. Neodpovídáme za žádná nedorozumění nebo nesprávné výklady vyplývající z použití tohoto překladu.\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"interpreter": {
|
|
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "Python 3.8.12 ('py38')",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.8.12"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "7673cd150d96c74c6d6011460094efb4",
|
|
"translation_date": "2025-08-29T15:47:40+00:00",
|
|
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
|
|
"language_code": "cs"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |