ACER_112024 ACER_112024 ACER_112024

ML v Pythone 11 – vytvorenie a učenie neurónovej siete na generovanie poviedok

0

Ukážeme vytvorenie a natrénovanie vlastnej neurónovej siete. Natrénujeme ju texte knihy. V reálnej praxi by sa trénovala na tisíckach kníh a ďaľších textov. Následne túto neurónovú sieť použijeme na generovanie textu krátkej poviedky. Zadáte prvých päť slov a algoritmus bude generovať ďalšie slová poviedky.

predchádzajúcej časti sme ukázali načítanie textu knihy, prevod na slová a vyčistenie od neabecedných znakov. Na natrénovanie použili knihu Rivers of Babylon od Petra Pišťanka, ktorú máme na Google disku v súbore Rivers of Babylon.txt. Aby bol kód v tomto článku kompletný uvedieme aj kód na načítanie knihy a vyčistenie textu

#načítanie knihy z Google drive
from google.colab import drive
 
drive.mount('/content/drive')
kniha1 = open('/content/drive/My Drive/ML Neuronova siet/Rivers of Babylon.txt', 'r')
kniha1 = kniha1.read()
 
import nltk
nltk.download('punkt')
from nltk.tokenize import word_tokenize
slova = word_tokenize(kniha1.lower())
 
import string
zoznam_slov = []
neabecedne_znaky = string.punctuation
neabecedne_znaky
 
for slovo in slova:
        for char in slovo:
            if char in neabecedne_znaky:
                slovo = slovo.replace(char, "")
        if slovo not in neabecedne_znaky:
            zoznam_slov.append(slovo)
print("Počet slov:", len(zoznam_slov), "unikátnych", len(set(zoznam_slov)))
 
slovnik= set(zoznam_slov) #unikátne slová
print("Slovník obsahuje", len(zoznam_slov),"slov, unikátnych slov je", len(slovnik))
indexy_slov = {slovo: i for i, slovo in enumerate(slovnik)}
zoznam_indexov_slov = [indexy_slov[slovo] for slovo in zoznam_slov]
 
davka=5
pocet_slov = len(zoznam_slov) #pocet slov knihy
trenovacia_mnozina = [([zoznam_indexov_slov[i], zoznam_indexov_slov[i+1], zoznam_indexov_slov[i+2], zoznam_indexov_slov[i+3], zoznam_indexov_slov[i+4]], zoznam_indexov_slov[i+5]) for i in range(pocet_slov-davka) ]
 
Takže toľko kompletný kód z minulého dielu, kde bol aj podrobne vysvetlený. Aby článok nebol príliš dlhý tak v tejto časti uvedieme len kódy na vytvorenie a natrénovanie NS a na predpoveď, vrátane zadávania prvých päť slov poviedky a generovania jej textu

Trénovaciu množinu máme pripravenú, môžeme sa pustiť do definovania a trénovania neurónovej siete.Postup:

  1. načítanie vstupných dát a prvotná inicializácia váh,
  2. pre vopred zvolený počet iterácií sa vykonajú nasledovné kroky:
  • posun vpred forward pass,
  • spätná propagácia,
  • úprava váh na základe gradientnej metódy,

Potom bude nasledovať  otestovanie úspešnosti klasifikácie

Posun vpred  (forward)pri predpovedaní informácie prúdia zo vstupu cez skryté vrstvy na výstup , čiže po predikciu výsledku. Na každom neuróne sa vstupy, čiže výstupy predchádzajúcich uzlov vynásobia príslušnými váhami spojení a následne sa sčítajú. Na záver sa aplikuje aktivačná funkcia neurónu. V našom prípade ako aktivačnú funkciu využijeme funkciu ReLu

Spätná propagácia – (backward propagation) rieši úpravu váh neurónových spojení tak, aby neurónová sieť poskytovala výstup, ktorý je bližší k požadovanému výsledku, čiže minimalizujeme stratovú funkciu. Tento proces prebieha opačne od výstupov k vstupom. Ako prvý sa vypočíta gradient váh poslednej výstupnej vrstvy a ako posledný sa vypočíta gradient váh prvej vrstvy. Výsledky výpočtu gradientu z príslušnej  vrstvy sa použijú pri výpočte gradientu pre predchádzajúcu vrstvu.

Najskôr uvedieme kompletný kód na vytvorenie triedy modelu, funkcie trénovania a kód na zadanie prvých piatich slov poviedky a v bydúcom pokračovaní vysvetlíme akú činnosť vykonávajú použité funkcie

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import time
 
embedding_dim = 5  #veľkosť embedding vektorov
# === Trieda definujúca model neurónovej siete ===
# Embedding (vložená vrstva) je skrytá vrstva NS ktorá
# mapuje vstupné informácie z vysoko do nízko rozmerného priestoru
 
class Model_NS(nn.Module):
  def __init__(self, pocet_slov, embedding_dim, davka):
    super(Model_NS, self).__init__()
    self.embeddings = nn.Embedding(pocet_slov, embedding_dim)
    self.linear1 = nn.Linear(davka * embedding_dim,128)  # lineárne transformácie ako vrstvy NS
    self.linear2 = nn.Linear(128, 512)
    self.linear3 = nn.Linear(512, pocet_slov)
 
   # pri predpovedaní informácie prúdia „dopredu“
   # zo vstupu cez skryté vrstvy na výstup
   # výstupom sú logaritmy pravdepodobností
  def forward(self, inputs):  # pri predpovedaní
    embs = self.embeddings(inputs).view((1,-1))
    out = F.relu(self.linear1(embs)) #lineárna aktivačná funkcia
    out = F.relu(self.linear2(out))
    out = self.linear3(out)
    log_pravdepodobnosti = F.log_softmax(out, dim=1) #konvertuje číselné hodnoty na pravdepodobnosti
    return log_pravdepodobnosti

Zistíme parametre modelu neurónovej siete

# výpis parametrov modelu
model = Model_NS(pocet_slov, embedding_dim, davka)
print(model.embeddings,"\n")
model
 
---výpis---
Embedding(90374, 5)
 
Model_NS(
  (embeddings): Embedding(90374, 5)
  (linear1): Linear(in_features=25, out_features=128, bias=True)
  (linear2): Linear(in_features=128, out_features=512, bias=True)
  (linear3): Linear(in_features=512, out_features=90374, bias=True)
)

 

Trénovanie modelu

# === trénovanie modelu ===
from torch.cuda import is_available
 
# Pokiaľ možno na GPU
if torch.cuda.is_available():
  device = torch.device("cuda:0")
else:
  device = torch.device("cpu")
 
priebezne_chyby = []  # chyby po každých 1000 krokoch trénovania
 
 
def trenovanie(model, trenovacie_data, n_iteracie, word_to_index):
  model.to(device)
  for i in range(n_iteracie):
    model.train()
    krok = 0
    perioda_vypisu = 1000
    akumulovana_chyba = 0  # na výpočet priemernej chyby po každých 1000 krokoch
   
    for feature, target in trenovacie_data:
      feature_tensor = torch.tensor([feature], dtype=torch.long)
      feature_tensor = feature_tensor.to(device)
      target_tensor = torch.tensor([target], dtype=torch.long)
      target_tensor = target_tensor.to(device)
      model.zero_grad()  #vynulovanie gradientov
      log_probs = model(feature_tensor)
      loss = criterion(log_probs, target_tensor)
      loss.backward()    #Spätný prechod pri aktualizácii parametrov
      optimizer.step()
      akumulovana_chyba += loss.item()
      krok+=1
 
      if krok % perioda_vypisu == 0:  #každých 1000 krokov
        model.eval()
        priemerna_chyba = akumulovana_chyba/perioda_vypisu
        priebezne_chyby.append(priemerna_chyba) #pridám do zoznamu
        print(i+1,"z",n_iteracie, "iterácií", krok,"krok z",pocet_slov,
               "Chyba: {:.3f}".format(priemerna_chyba))
        akumulovana_chyba=0
    model.train()
  return model

Spustenie trénovania pre daný počet iterácií

model = Model_NS(pocet_slov, embedding_dim, davka)
criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(),lr=0.01)
 
n_iteracie = 2
 
start_time = time.time()
 
model = trenovanie(model, trenovacia_mnozina, n_iteracie, indexy_slov)
print("Trénovanie modelu trvalo {} minút".format(round((start_time - time.time())/60),2))
 
# Doplnenie modelu
model.average_loss = priebezne_chyby
model.optimizer_state = optimizer.state_dict()
model.word_to_index = indexy_slov,
model.index_to_word = {i: word for i, word in enumerate(slovnik)}

Graf priebežných chýb počas trénovania

import pandas as pd
 
loss_plot = pd.DataFrame(priebezne_chyby)
loss_plot.plot()

Funkcia na predpovedanie slov poviedky

import pandas as pd
import random
 
 
def predpoved(model, zaciatok_poviedky ,dlzka_poviedky ,top_slova):
    # top_slova - slovo sa vyberie náhodne z top_k počtu
 
    # Prvých 5 slov do poviedky
    vstupy = (zaciatok_poviedky.lower()).split(" ") # Prvých 5 slov
    for i in vstupy:
        poviedka.append(i)
    for i in range(dlzka_poviedky):
        # slová na čísla (indexy)
        vstupy_ix = torch.tensor([indexy_slov[word] for word in vstupy], dtype=torch.long)
        vstupy_ix = vstupy_ix.to(device)
        with torch.no_grad(): # nevoláme Tensor.backward() nemusíme počítať gradienty
            output = model.double().forward(vstupy_ix)
        ps = torch.exp(output)
        top_kombinacia_indexy = ps.topk(top_slova, sorted=True)
        #top_n najpravdepodobnejších slov
        topk_ps = top_kombinacia_indexy[0][0]
        top_ntica = top_kombinacia_indexy[1][0]
        top_ntica = [model.index_to_word[int(i)] for i in top_ntica]
        # náhodný výber slova z top_n
        next_word = random.choice(top_ntica)
        vstupy = vstupy[1:]  # pridanie slova do vstupov a posunutie
        vstupy.append(next_word)
        poviedka.append(next_word)  # pridanie slova do poviedky
    return poviedka

Generovanie poviedky

zaciatok_poviedky = input('Zadaj prvých {} slov poviedky:\n'.format(davka))
 
top_slova = 3 # Slovo sa vyberie náhodne z top_k počtu
dlzka_poviedky = 20
poviedka = []
 
if torch.cuda.is_available():
    device = torch.device("cuda:0")
else:
    device = torch.device("cpu")
 
# osetrenie vstupu, treba zadať 5 slov, ktoré model pozná
try:
    predikcia = predpoved(model, zaciatok_poviedky, dlzka_poviedky, top_slova)
except KeyError as error:
    print('niektorému zo slov zatiaľ nerozumiem\n zadaj slová z kníh na ktorých som sa učil nie ', error)
    if poviedka[0] != "":
        poviedka = poviedka[len(zaciatok_poviedky):]
    zaciatok_poviedky = input('zadaj slová ktorým rozumiem\n')
    predikcia = predpoved(model, zaciatok_poviedky, dlzka_poviedky, top_slova)
except KeyError and RuntimeError:
    if poviedka[0] != "":
        poviedka = poviedka[len(zaciatok_poviedky):]
    zaciatok_poviedky = input('Zadal si {} slov namiesto {}!\n\nZadaj presný počet slov\n'.format(len(zaciatok_poviedky.split(" ")), 5))
    predikcia = predpoved(model, zaciatok_poviedky, dlzka_poviedky, top_slova)
 
print('\n=== Poviedka ===')
print(" ".join(poviedka))

Vygenerovaný text po dvoch iteráciách trénovania po zadaní piatich slov:“ obloha čoraz častejšie dostáva farbu“

obloha čoraz častejšie dostáva farbu na nich a po sa do tmy rácz sa z svoju a už si s z a rácz sa mu do vily za sa a rácz na všetko do tmy rácz je rácz až a rácz sa do svoju rácz sa už do na hodinky sa už sa na svoju

Vygenerovaný text po desiatich iteráciách trénovania po zadaní rovnakých prvých piatich slov

obloha čoraz častejšie dostáva farbu na nevinných no trolejbusom do vily že ho do nechápavo až vám keď nesie oblokom fitness že kvapku rácz s prsty ktorý stisnúť alebo jemu veď až na všetko fredyho a o ústam vzrušenia hneď rácz si sa necítia sebou pred sebou sa však a už dávno opäť stučnel stáť

Po ďalšom spustení vznikne iný text

obloha čoraz častejšie dostáva farbu pred všetkých života o hotelovej veľa rácz mohol rácz vzdiali ako sám niekedy strach s sa platnosťou že za slobodu a čo si chceli rácz sem no kedysi že nemá to že si sa im skončí lesknú a pozerá si dnes stôl každý mu nikdy pred svoju sa stane naučil

V budúcom pokračovaní podrobne popíšeme funkcie použité na vytvorenie a trénovvanie neurónovej siete a na generovanie textu

Rekapitulácia doterajších dielov

Strojové učenie v Pythone 1 – prostredie Google Colab

Strojové učenie v Pythone 2 – knižnica Pandas na prácu s údajmi

ML v Pythone 3 – export a import údajov vo formáte CSV a Excel

Strojové učenie v Pythone 4 – práca s údajmi

ML v Pythone 5 – vizualizácia údajov pomocou grafov

ML v Pythone 6 – animované grafy a ich export ako video

ML v Pythone 7 – Využitie grafickej karty NVIDIA na výpočtovo náročné úlohy

ML v Pythone 8 – príklad rozpoznávanie obrazu

ML v Pythone 9 – trénovanie neurónu

ML v Pythone 10 – trénovanie neurónovej siete, príprava

Luboslav Lacko

Všetky autorove články
Python strojove ucenie neuronova siet

Pridať komentár

Mohlo by vás zaujímať

Mohlo by vás zaujímať