Hlavní navigace

Framework Torch: konfigurace konvolučních neuronových sítí

Pavel Tišnovský

Dnes si ukážeme, jak dobře či naopak špatně se nám podaří natrénovat konvoluční neuronovou síť tak, aby dobře rozpoznávala číslice v zašuměných či jinak upravených rastrových obrázcích.

Doba čtení: 28 minut

   4.3 Modul nn_validators

5. Hlavní modul projektu

6. Průběh učení sítě

7. Vliv šumu vneseného do obrázků na odhad sítě

8. Postupné zvyšování šumu a odhad sítě vynesený do grafů

9. Snížení počtu iterací při tréninku sítě

10. Síť naučená s malým počtem iterací a grafy odhadu sítě pro postupné zvyšování šumu

11. Vliv roztřesení obrázků na odhad sítě

12. Postupné zvyšování míry rozstřesení a odhad sítě vynesený do grafů

13. Repositář s demonstračními příklady

14. Odkazy na Internetu

1. Framework Torch: konfigurace konvolučních neuronových sítí

V předchozím článku seriálu o frameworku Torch jsme si ukázali způsob konstrukce i natrénování konvoluční neuronové sítě, která relativně bez problémů dokázala rozpoznat obrázky číslic, které byly zašuměné (s volitelnou mírou šumu). Dnešní článek bude tématicky rozdělen na dvě části. V první části nejdříve vytvoříme z příkladu ukázaného minule projekt rozdělený na několik modulů, protože s takto navrženým projektem se nám bude mnohem lépe pracovat. Následně zjistíme závislost přesnosti odhadu neuronové sítě na míře šumu ve validačních obrázcích, na počtu iterací provedených při tréninku sítě a nakonec si ukážeme, jak dobře (či v některým případech spíše špatně) dokáže neuronová síť klasifikovat číslice v obrázcích, které byly „rozstřeseny“ algoritmem jitteringu. Všechny úpravy budou prováděny na jediném místě, konkrétně v hlavním skriptu celého projektu (viz navazující kapitoly).

Tentýž projekt použije i v dalších částech seriálu, pouze v něm nepatrně upravíme parametry konvoluční sítě (velikost konvolučního jádra atd.)

2. Ucelený projekt pro trénink a validaci konvoluční neuronové sítě

Celý projekt s konvoluční sítí je rozdělen do tří částí:

  1. Moduly s konstruktorem neuronové sítě, funkcemi pro její trénink a taktéž funkcemi pro validaci natrénované sítě. Tyto moduly jsou uloženy v podadresáři nn.
  2. Moduly pro vygenerování trénovacích i validačních obrázků, aplikaci šumu, roztřesení, posunutí a v neposlední řadě i uložení obrázků do externích souborů a konverzi obrázků do tenzoru. Tyto moduly jsou uloženy v podadresáři image_lib.
  3. Hlavní modul celého projektu, který obsahuje konstanty použité jak pro konstrukci sítě, tak i pro vytváření obrázků, trénink sítě atd.

Způsob rozdělení projektu do jednotlivých adresářů a souborů je následující:

├── convolution_network_noisy_images.lua
├── image_lib
│   ├── image_filters.lua
│   ├── image_generator.lua
│   ├── image_renderer.lua
│   └── image_writer.lua
└── nn
    ├── nn_constructor.lua
    ├── nn_trainer.lua
    └── nn_validators.lua

3. Moduly umístěné v adresáři image_lib

V této kapitole je stručně popsána čtveřice modulů umístěných do adresáře image_lib.

3.1 Modul image_renderer

Modul image_renderer obsahuje funkci nazvanou generate_image, kterou již dobře známe. Připomeňme si, že tato funkce slouží pro vytvoření dvourozměrné tabulky představující monochromatický rastrový obrázek číslice 0 až 9. Originální rozlišení generovaných obrázků je 8×8 pixelů, ovšem obrázky lze v případě potřeby zvětšit využitím celočíselného parametru scale, takže je možné vytvořit obrázky o rozlišení 16×16 pixelů, 24×24 pixelů atd. atd. Dále je možné specifikovat úroveň tmavých a světlých pixelů, protože se nemusí jednat o zcela černou či naopak plně bílou barvu (naopak to může neuronovou síť dobře zmást):

digits = {
    {0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00 },
    {0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00 },
    {0x00, 0x3C, 0x66, 0x30, 0x18, 0x0C, 0x7E, 0x00 },
    {0x00, 0x7E, 0x30, 0x18, 0x30, 0x66, 0x3C, 0x00 },
    {0x00, 0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x00 },
    {0x00, 0x7E, 0x06, 0x3E, 0x60, 0x66, 0x3C, 0x00 },
    {0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00 },
    {0x00, 0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x00 },
    {0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00 },
    {0x00, 0x3C, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00 },
}
 
 
function get_codes_for_digit(digit)
    if digit < 0 or digit > 9 then
        return nil
    end
    return digits[digit+1]
end
 
 
function setpixel(image, x, y, bit, white_level, black_level)
    if bit==1 then
        image[y][x] = white_level
    else
        image[y][x] = black_level
    end
end
 
 
function generate_image(digit, scale, black_level, white_level)
    local codes = get_codes_for_digit(digit)
    local image = {}
    local y_offset = 1
    for _, code in ipairs(codes) do
        for y = 1,scale do
            -- vytvorit dalsi obrazovy radek
            image[y_offset] = {}
            local x_offset = 1
            local byte = code
            -- vypocet barev jednotlivych pixelu v obrazku
            for _ = 1,8 do
                -- zjistit hodnotu n-teho bitu + posun bajtu s maskou znaku
                local bit = byte % 2
                byte = (byte - bit)/2
                for x = 1,scale do
                    -- obarveni konkretniho pixelu
                    setpixel(image, x_offset, y_offset, bit, white_level, black_level)
                    x_offset = x_offset + 1
                end
            end
            y_offset = y_offset + 1
        end
    end
    return image
end

3.2 Modul image_filters

Další modul se jmenuje image_filters a jak již jeho název napovídá, obsahuje tento modul funkce sloužící pro aplikaci různých filtrů na již existující obrázek (tedy na dvourozměrnou tabulku). Nalezneme zde funkci pro posun obrázku v horizontálním a/nebo vertikálním směru, pro zašumění obrázku Gaussovým šumem i funkci pro roztřesení pixelů (jitter). Zejména tato poslední funkce je zajímavá, protože konvoluční neuronová síť bude mít s rozpoznáním a klasifikací těchto obrázků velké problémy (ostatně při větší míře roztřesení bude mít problémy i člověk :-):

function gaussian(mean, variance)
    return math.sqrt(-2 * variance * math.log(math.random())) *
           math.cos(2 * variance * math.pi * math.random()) + mean
end
 
 
function bound(value, min_value, max_value)
    return math.max(min_value, math.min(value, max_value))
end
 
 
function apply_noise(source_image, variance)
    local target_image = {}
    for y, row in ipairs(source_image) do
        target_image[y] = {}
        for x, pixel in ipairs(row) do
            local delta = math.floor(gaussian(0, variance))
            target_image[y][x] = bound(pixel + delta, 0, 255)
        end
    end
    return target_image
end
 
 
function apply_jitter(source_image, variance)
    local target_image = {}
    local max_x = #source_image[1]
    local max_y = #source_image
 
    for y = 1,max_y do
        target_image[y] = {}
        for x = 1,max_x do
            xs = x + math.floor(gaussian(0, variance))
            ys = y + math.floor(gaussian(0, variance))
            xs = bound(xs, 1, max_x)
            ys = bound(ys, 1, max_y)
            target_image[y][x] = source_image[ys][xs]
        end
    end
    return target_image
end
 
 
function translate(source_image, x_offset, y_offset)
    local target_image = {}
    local max_x = #source_image[1]
    local max_y = #source_image
 
    for y = 1,max_y do
        target_image[y] = {}
        for x = 1,max_x do
            xs = x - x_offset
            ys = y - y_offset
            xs = bound(xs, 1, max_x)
            ys = bound(ys, 1, max_y)
            target_image[y][x] = source_image[ys][xs]
        end
    end
    return target_image
end

3.3 Modul image_generator

Modul image_generator bude přímo používán jak při tréninku, tak i při validaci neuronové sítě, protože jsou v něm implementovány funkce sloužící pro vygenerování trénovacích a validačních obrázků. Tyto obrázky mají jednoduchou strukturu, protože se jedná o pouhé dvourozměrné tabulky (tabulka se v jazyku Lua používá jak pro implementaci běžného pole, tak i pro asociativní pole):

function generate_training_images(scale, noise_variances, repeat_count, black_level, white_level, export_images)
    local training_images = {}
    for _, noise_variance in ipairs(noise_variances) do
        for digit = 0, 9 do
            for i = 1, repeat_count do
                local training_image = generate_image(digit, scale, black_level, white_level)
                training_image = apply_noise(training_image, noise_variance)
                table.insert(training_images, {digit=digit,
                                               data=training_image})
                if export_images then
                    local filename = string.format("training_%d_%d_%d.pgm", digit, noise_variance, i)
                    write_image(filename, training_image)
                end
            end
        end
    end
    return training_images
end
 
 
function noisy_image_filename(digit, noise_variance)
    return string.format("validation_%d_noise_%d.pgm", digit, noise_variance)
end
 
 
function generate_noisy_image_for_validation(digit, scale, noise_variance, black_level, white_level, export_image)
    local validation_image = generate_image(digit, scale, black_level, white_level)
    validation_image = apply_noise(validation_image, noise_variance)
    if export_image then
        local filename = noisy_image_filename(digit, noise_variance)
        write_image(filename, validation_image)
    end
    return validation_image
end
 
 
function jitter_image_filename(digit, jitter_variance)
    return string.format("validation_%d_jitter_%d.pgm", digit, jitter_variance)
end
 
 
function generate_jittered_image_for_validation(digit, scale, jitter_variance, black_level, white_level, export_image)
    local validation_image = generate_image(digit, scale, black_level, white_level)
    validation_image = apply_jitter(validation_image, jitter_variance)
    if export_image then
        local filename = jitter_image_filename(digit, jitter_variance)
        write_image(filename, validation_image)
    end
    return validation_image
end

Pro převod obrázků do trojrozměrného tenzoru se používá funkce image2tensor. A proč vlastně potřebujeme trojrozměrný vektor a nikoli vektor dvourozměrný? Na vstupu konvolučních neuronových sítí se používají trojrozměrné tenzory z toho důvodu, že se původní obrázky s pixely reprezentovanými v barvovém prostoru RGB, YUV, YCbCr atd. rozdělí do jednotlivých bitových rovin, kde každá rovina obsahuje pouze zvolenou složku pixelu (například tedy červenou barvovou složku). V našem případě máme zjednodušenou práci, protože používáme monochromatické obrázky, takže trojrozměrný tenzor bude mít velikost první dimenze rovnu jedné:

function image2tensor(image)
    local table3d = {image}
    return torch.Tensor(table3d):double()
end

3.4 Modul image_writer

Modul pojmenovaný image_writer obsahuje jedinou funkci sloužící pro uložení obrázku do externího souboru ve formátu PGM (Portable GrayMap), který dokáže knihovna Torch načíst. Ve skutečnosti však v dnešním projektu použijeme tuto funkci jen pro ladicí účely, protože generované a ukládané obrázky nebudeme načítat zpět, ale přímo si je (v operační paměti) převedeme do trojrozměrného tenzoru:

function write_image(filename, image)
    local fout = io.open(filename, "w")
    if not fout then
        return
    end
 
    -- rozliseni obrazku
    local x_resolution = #image[1]
    local y_resolution = #image
 
    -- zapis hlavicky
    fout:write("P2\n")
    fout:write(x_resolution)
    fout:write(" ")
    fout:write(y_resolution)
    fout:write("\n255\n")
 
    -- zapis jednotlivych pixelu
    for j, row in ipairs(image) do
        for i, pixel in ipairs(row) do
            -- logika pro oddeleni hodnot
            if i ~= 1 then
                fout:write(" ")
            end
            fout:write(pixel)
        end
        -- odradkovani neni nutne
        fout:write("\n")
    end
 
    fout:close()
end

4. Moduly umístěné v adresáři nn

V této kapitole je stručně popsána čtveřice modulů umístěných do adresáře nn.

4.1 Modul nn_constructor

Tento modul obsahuje funkci construct_neural_network určenou pro vytvoření konvoluční neuronové sítě se specifikovanou konfigurací – rozlišením vstupních obrázků, počtem rovin (atributů), počtem skrytých neuronů ve druhé polovině sítě atd. S významem jednotlivých konfiguračních parametrů jsme se seznámili v předchozím článku:

function calculate_size_after_convolution(input_size, middle_planes, convolution_kernel_size)
    local size = input_size
    for i=1,#middle_planes do
        -- velikost po projiti konvolucni vrstvou
        size = size - convolution_kernel_size + 1
        -- velikost po projiti pooling vrstvou
        size = size / 2
    end
    return size
end
 
 
function construct_neural_network(width, height, input_planes, middle_planes,
                                  hidden_neurons, output_neurons,
                                  convolution_kernel_size, pooling_size, pooling_step)
    local network = nn.Sequential()
 
    local size_x = calculate_size_after_convolution(width, middle_planes, convolution_kernel_size)
    local size_y = calculate_size_after_convolution(height, middle_planes, convolution_kernel_size)
 
    print("Size x: " .. size_x)
    print("Size y: " .. size_y)
 
    -- prvni konvolucni vrstva ocekavajici na vstupu 3D tenzor
    -- o velikosti:
    -- INPUT_PLANES x vyska x sirka
    --
    -- vysledkem je 3D tenzor o velikosti:
    -- MIDDLE_PLANES_1 x (vyska - CONVOLUTION_KERNEL_SIZE + 1) x (sirka - CONVOLUTION_KERNEL_SIZE + 1) 
    network:add(nn.SpatialConvolution(input_planes, middle_planes[1], convolution_kernel_size, convolution_kernel_size))
 
    -- nyni mame mezivysledky 64 x (vyska-5+1) x (sirka-5+1)
 
    -- nelinearni funkce
    network:add(nn.Tanh())
 
    -- hledani maxima v regionech o velikosti 2x2 pixely
    -- s krokem nastavenym na 2 pixely v horizontalnim i 2 pixely ve vertikalnim smeru
    network:add(nn.SpatialMaxPooling(pooling_size, pooling_size, pooling_step, pooling_step))
 
    -- druha konvolucni vrstva ocekavajici na vstupu 3D tenzor
    -- o velikosti MIDDLE_PLANES_1 x vyska x sirka
    network:add(nn.SpatialConvolution(middle_planes[1], middle_planes[2], convolution_kernel_size, convolution_kernel_size))
 
    -- nelinearni funkce
    network:add(nn.Tanh())
 
    -- opetovne hledani maxima v regionech o velikosti 2x2 pixely
    -- s krokem nastavenym na 2 pixely v horizontalnim i 2 pixely ve vertikalnim smeru
    network:add(nn.SpatialMaxPooling(pooling_size, pooling_size, pooling_step, pooling_step))
 
    -- zmena tvaru: z 3D tenzoru AxBxC na 1D tenzor s A*B*C elementy
    network:add(nn.View(middle_planes[2]*size_x*size_y))
 
    -- bezne vrstvy, jak je jiz zname
    network:add(nn.Linear(middle_planes[2]*size_x*size_y, hidden_neurons))
 
    -- pridana nelinearni funkce
    network:add(nn.ReLU())
 
    -- bezne vrstvy, jak je jiz zname
    network:add(nn.Linear(hidden_neurons, output_neurons))
 
    return network
end

4.2 Modul nn_trainer

V modulu nn_trainer nalezneme především funkci volanou pro vlastní trénink sítě (train_neural_network), ale taktéž funkci prepare_training_data, která ze sady vygenerovaných testovacích obrázků vytvoří tenzor určený pro trénink sítě. Připomeňme si, že tento tenzor obsahuje dvojice vstup:očekávaný-výstup, přičemž vstupem jsou jednotlivé obrázky (převedené na tenzory) a výstupem pak tenzor s váhou odhadu jednotlivých číslic:

function train_neural_network(network, training_data, learning_rate, max_iteration)
    local criterion = nn.MSECriterion()
    local trainer = nn.StochasticGradient(network, criterion)
    trainer.learningRate = learning_rate
    trainer.maxIteration = max_iteration
    trainer:train(training_data)
end
 
 
function generate_expected_output(digit)
    local result = torch.zeros(DIGITS)
    result[digit+1] = 1
    return result
end
 
 
function prepare_training_data(scale, noise_variances, repeat_count,
                               black_level, white_level, export_images)
    local training_images = generate_training_images(scale, noise_variances, repeat_count,
                                                     black_level, white_level, export_images)
    local training_data_size = #training_images
    local training_data = {}
    function training_data:size() return training_data_size end
 
    for i, training_image in ipairs(training_images) do
        local input = image2tensor(training_image.data)
        local digit = training_image.digit
        local output = generate_expected_output(digit)
        training_data[i] = {input, output}
    end
 
    return training_data
end

4.3 Modul nn_validators

Tento modul je ze všech popisovaných modulů nejrozsáhlejší, protože obsahuje několik funkcí, které nejenom validují odhady natrénované neuronové sítě, ale také vykreslí grafy s váhami (pravděpodobnostmi) jednotlivých odhadů. Význam jednotlivých funkcí si ukážeme v navazujících kapitolách:

function find_largest_item(tensor)
    local index = -1
    local value = -math.huge
    for i = 0, 9 do
        if tensor[i+1] > value then
            index = i
            value = tensor[i+1]
        end
    end
    return index, value
end
 
 
function plot_graph(filename, values)
    gnuplot.pngfigure(filename)
    gnuplot.imagesc(values, 'color')
    gnuplot.raw("set terminal pngcairo size 1280, 480")
    gnuplot.plotflush()
    gnuplot.close()
end
 
 
function validate_neural_network_using_noise_images(network, scale, noise, black_level, white_level, export_images)
    local errors = 0
    local count = 0
    for expected_digit = 0, 9 do
        local input = image2tensor(generate_noisy_image_for_validation(expected_digit, scale, noise, black_level, white_level, export_images))
        local output = network:forward(input)
        local result, weight = find_largest_item(output)
        if expected_digit ~= result then
            errors = errors + 1
        end
        print(expected_digit, result, expected_digit==result, weight)
        count = count + 1
    end
    print("---------------------")
    print("Errors: " .. errors)
    print("Error rate: " .. 100.0*errors/count .. "%")
end
 
 
function validate_neural_network_variable_noise(network, scale, digit,
                                                black_level, white_level, export_images)
    local max_noise = 1000
    local values = torch.Tensor(max_noise, DIGITS)
 
    for noise_variance = 1, max_noise do
        local input = image2tensor(generate_noisy_image_for_validation(digit, scale, noise_variance,
                                                                       black_level, white_level, export_images))
        local output = network:forward(input)
        values[noise_variance] = output
    end
    local output_graph_filename = string.format("digit%d_variable_noise.png", digit)
    plot_graph(output_graph_filename, values:t())
end
 
 
function validate_neural_network_using_jittered_images(network, scale, jitter_variance, black_level, white_level, export_images)
    local errors = 0
    local count = 0
    for expected_digit = 0, 9 do
        local input = image2tensor(generate_jittered_image_for_validation(expected_digit, scale, jitter_variance,
                                                                          black_level, white_level, export_images))
        local output = network:forward(input)
        local result, weight = find_largest_item(output)
        if expected_digit ~= result then
            errors = errors + 1
        end
        print(expected_digit, result, expected_digit==result, weight)
        count = count + 1
    end
    print("---------------------")
    print("Errors: " .. errors)
    print("Error rate: " .. 100.0*errors/count .. "%")
end
 
 
function validate_neural_network_variable_jitter(network, scale, digit,
                                                 black_level, white_level, export_images)
    local max_jitter = 20
    local values = torch.Tensor(max_jitter, DIGITS)
 
    for jitter_variance = 1, max_jitter do
        local input = image2tensor(generate_jittered_image_for_validation(digit, scale, jitter_variance,
                                                                          black_level, white_level, export_images))
        local output = network:forward(input)
        values[jitter_variance] = output
    end
 
    local output_graph_filename = string.format("digit%d_variable_jitter.png", digit)
    plot_graph(output_graph_filename, values:t())
end

5. Hlavní modul projektu

Hlavní modul celého projektu je uložen v souboru nazvaném convolution_network_noisy_images.lua. Jedná se o jediný modul, který budeme postupně modifikovat, protože právě zde jsou uloženy všechny konfigurační parametry – rozměry trénovacích i validačních obrázků, počet iterací při tréninku sítě, architektura navržené konvoluční sítě atd. Na konci skriptu je umístěn programový kód určený pro validaci; část kódu bude vždycky zakomentovaná:

require("nn")
require("image")
require("gnuplot")
 
require("image_lib/image_renderer")
require("image_lib/image_writer")
require("image_lib/image_filters")
require("image_lib/image_generator")
 
require("nn/nn_constructor")
require("nn/nn_trainer")
require("nn/nn_validators")
 
 
-- globalni nastaveni
DIGITS = 10
 
 
-- parametry obrazku
WIDTH = 32
HEIGHT = 32
BLACK_LEVEL = 64
WHITE_LEVEL = 192
 
 
-- parametry neuronove site
INPUT_PLANES = 1
 
MIDDLE_PLANES = {64, 64}
 
HIDDEN_NEURONS = 100
OUTPUT_NEURONS = 10
 
-- parametry konvolucni vrstvy
CONVOLUTION_KERNEL_SIZE = 5
 
-- parametry pooling vrstvy
POOLING_SIZE = 2
POOLING_STEP = 2
 
-- parametry pro uceni neuronove site
MAX_ITERATION = 20
LEARNING_RATE = 0.01
 
-- dalsi parametry
EXPORT_IMAGES = true
 
 
 
network = construct_neural_network(WIDTH, HEIGHT, INPUT_PLANES, MIDDLE_PLANES,
                                   HIDDEN_NEURONS, OUTPUT_NEURONS,
                                   CONVOLUTION_KERNEL_SIZE, POOLING_SIZE, POOLING_STEP)
print(network)
 
NOISE_VARIANCES = {0, 10, 20, 50}
REPEAT_COUNT = 3
SCALE = 4
 
training_data = prepare_training_data(SCALE, NOISE_VARIANCES, REPEAT_COUNT,
                                      BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
 
train_neural_network(network, training_data, LEARNING_RATE, MAX_ITERATION)
 
validate_neural_network_using_noise_images(network, SCALE, 100, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 1000, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 2500, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 5000, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
 
os.exit(1)
 
for digit = 0, 9 do
    print("splot for digit " .. digit)
    validate_neural_network_variable_noise(network, SCALE, digit, BLACK_LEVEL, WHITE_LEVEL)
end
 
 
validate_neural_network_using_jittered_images(network, SCALE, 1, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_jittered_images(network, SCALE, 2, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_jittered_images(network, SCALE, 5, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_jittered_images(network, SCALE, 10, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_jittered_images(network, SCALE, 20, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
 
for digit = 0, 9 do
    print("splot for digit " .. digit)
    validate_neural_network_variable_jitter(network, SCALE, digit, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
end

6. Průběh učení sítě

Nejprve zkonstruujeme novou konvoluční neuronovou síť a necháme si vytisknout její strukturu:

network = construct_neural_network(WIDTH, HEIGHT, INPUT_PLANES, MIDDLE_PLANES,
                                   HIDDEN_NEURONS, OUTPUT_NEURONS,
                                   CONVOLUTION_KERNEL_SIZE, POOLING_SIZE, POOLING_STEP)
print(network)

Struktura vypadá následovně:

nn.Sequential {
  [input -> (1) -> (2) -> (3) -> (4) -> (5) -> (6) -> (7) -> (8) -> (9) -> (10) -> output]
  (1): nn.SpatialConvolution(1 -> 64, 5x5)
  (2): nn.Tanh
  (3): nn.SpatialMaxPooling(2x2, 2,2)
  (4): nn.SpatialConvolution(64 -> 64, 5x5)
  (5): nn.Tanh
  (6): nn.SpatialMaxPooling(2x2, 2,2)
  (7): nn.View(1600)
  (8): nn.Linear(1600 -> 100)
  (9): nn.ReLU
  (10): nn.Linear(100 -> 10)
}

Pro učení sítě využijeme sadu trénovacích obrázků, v nichž jsou jednotlivé číslice částečně zašuměny. Obrázky nám dodá funkce generate_training_images volaná z funkce prepare_training_data. Následně pouze zavoláme funkci pro tréning sítě:

NOISE_VARIANCES = {0, 10, 20, 50}
REPEAT_COUNT = 3
SCALE = 4
 
training_data = prepare_training_data(SCALE, NOISE_VARIANCES, REPEAT_COUNT,
                                      BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
 
train_neural_network(network, training_data, LEARNING_RATE, MAX_ITERATION)

Průběh učení naznačuje, že chyba poměrně rychle klesá, tj. síť se dobře učí (trénuje):

# StochasticGradient: training
# current error = 0.076597900332967
# current error = 0.043665505304942
# current error = 0.024003421497494
# current error = 0.012576232816519
# current error = 0.0064539779976954
# current error = 0.0033509310961456
# current error = 0.0017758012965287
# current error = 0.00095901274943609
# current error = 0.00054799449188649
# current error = 0.00033867089602328
...
...
...
# current error = 9.2518066766304e-06
# current error = 9.1928849962747e-06
# current error = 9.1277176146737e-06
# current error = 9.0550642351311e-06
# current error = 8.9960301984168e-06
# current error = 8.9447400204176e-06
# current error = 8.8771303483088e-06
# StochasticGradient: you have reached the maximum number of iterations
# training error = 8.8771303483088e-06

7. Vliv šumu vneseného do obrázků na odhad sítě

Podívejme se nyní na to, jak dobře či naopak špatně bude naše konvoluční neuronová síť rozpoznávat zašuměné obrázky. Pro relativně malé úrovně šumu by měl být odhad velmi dobrý, ostatně přesně na takové obrázky byla síť natrénována. Ovšem s rostoucí mírou šumu se bude odhad zhoršovat, což ale platí i pro člověka (uvidíte sami při pohledu na obrázky, jak „snadné“ to někdy je).

Postupně spustíme následující čtveřici funkcí:

validate_neural_network_using_noise_images(network, SCALE, 100, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 1000, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 2500, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)
validate_neural_network_using_noise_images(network, SCALE, 5000, BLACK_LEVEL, WHITE_LEVEL, EXPORT_IMAGES)

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 100:

0       0       true    0.92901758582303
1       1       true    0.914798882625
2       2       true    0.90179615034807
3       3       true    0.94704201146714
4       4       true    0.95418884655987
5       5       true    0.94832194965547
6       6       true    0.90328739214275
7       7       true    0.90261509534692
8       8       true    0.9278343906419
9       9       true    0.90061346217945
---------------------
Errors: 0
Error rate: 0%

Obrázek 1: Validační obrázky s úrovní šumu (rozptylem) nastaveným na hodnotu 100.

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 1000:

0       0       true    0.71679340667254
1       1       true    0.46674692091274
2       2       true    0.68365225813303
3       3       true    0.61487049242692
4       4       true    0.59118905950355
5       5       true    0.66081431702508
6       6       true    0.4819882432665
7       7       true    0.44086099586168
8       8       true    0.51702969306145
9       9       true    0.44992202255959
---------------------
Errors: 0
Error rate: 0%

Obrázek 2: Validační obrázky s úrovní šumu (rozptylem) nastaveným na hodnotu 1000.

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 2500 (zde již došlo k chybnému odhadu):

0       0       true    0.49964226424318
1       2       false   0.31674926934743
2       2       true    0.54998900222653
3       3       true    0.42461270684221
4       4       true    0.42353880107808
5       5       true    0.41895086570902
6       6       true    0.44201805138505
7       7       true    0.35794764982959
8       8       true    0.49182046062964
9       9       true    0.36450208880863
---------------------
Errors: 1
Error rate: 10%

Obrázek 3: Validační obrázky s úrovní šumu (rozptylem) nastaveným na hodnotu 2500.

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 5000 (zde se síť spletla třikrát, ovšem sami se podívejte na obrázky):

0       0       true    0.35376672201379
1       2       false   0.29025898930581
2       2       true    0.60442824010849
3       3       true    0.28843739459533
4       4       true    0.31849058724296
5       5       true    0.29310021725369
6       2       false   0.31115736833006
7       2       false   0.44993918004451
8       8       true    0.44275804344617
9       9       true    0.31744106228322
---------------------
Errors: 3
Error rate: 30%

Obrázek 4: Validační obrázky s úrovní šumu (rozptylem) nastaveným na hodnotu 5000.

8. Postupné zvyšování šumu a odhad sítě vynesený do grafů

Zajímavé bude sledovat, jak se bude odhad sítě zhoršovat s rostoucím šumem (což jsme si již ukázali u „obyčejné“ neuronové sítě). Proto použijeme již výše popsanou funkci nazvanou validate_neural_network_va­riable_noise z modulu nn_validators, v níž vykreslíme podobné grafy pro verifikační data, ovšem nyní se bude s každým měřením zvětšovat míra šumu až na hodnotu 1024.

Výsledkem je pouhých deset grafů pro deset číslic, takže si je uvedeme všechny. Z grafů je patrné, že si je konvoluční síť v odhadu číslic velmi jistá, a to i pro vysoké úrovně šumu:

Obrázek 5: Odhad pro číslici 0 pro střední hodnotu šumu od 0 do 1000.

Obrázek 6: Odhad pro číslici 1 pro střední hodnotu šumu od 0 do 1000.

Obrázek 7: Odhad pro číslici 2 pro střední hodnotu šumu od 0 do 1000.

Obrázek 8: Odhad pro číslici 3 pro střední hodnotu šumu od 0 do 1000.

Obrázek 9: Odhad pro číslici 4 pro střední hodnotu šumu od 0 do 1000.

Obrázek 10: Odhad pro číslici 5 pro střední hodnotu šumu od 0 do 1000.

Obrázek 11: Odhad pro číslici 6 pro střední hodnotu šumu od 0 do 1000.

Obrázek 12: Odhad pro číslici 7 pro střední hodnotu šumu od 0 do 1000.

Obrázek 13: Odhad pro číslici 8 pro střední hodnotu šumu od 0 do 1000.

Obrázek 14: Odhad pro číslici 9 pro střední hodnotu šumu od 0 do 1000.

9. Snížení počtu iterací při tréninku sítě

Pokud snížíme počet iterací až na hodnotu 10 (což je obecně velmi málo, typicky se používá hodnota o dva až tři řády vyšší), kupodivu se odhad sítě razantně nezhorší, alespoň ve chvíli, kdy se snažíme rozpoznat a kvalifikovat zašuměné obrázky. Ostatně se o tom můžeme relativně snadno přesvědčit:

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 100:

0       0       true    0.9007039796097
1       1       true    0.84988676812512
2       2       true    0.88670165441306
3       3       true    0.85716831216868
4       4       true    0.96028286120409
5       5       true    0.89073656326969
6       6       true    0.85229764211598
7       7       true    0.84117398125546
8       8       true    0.78770696900435
9       9       true    0.91263127172923
---------------------
Errors: 0
Error rate: 0%

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 1000:

0       0       true    0.56502721095338
1       1       true    0.57947685218729
2       2       true    0.73469718583774
3       3       true    0.59729417113505
4       4       true    0.65922615777093
5       5       true    0.55426296544767
6       6       true    0.56374782525079
7       7       true    0.45865045184205
8       8       true    0.36333484963251
9       9       true    0.72257957894121
---------------------
Errors: 0
Error rate: 0%

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 2500:

0       0       true    0.36889956402128
1       1       true    0.397362275251
2       2       true    0.54447832474612
3       3       true    0.58269293319752
4       4       true    0.49334754817544
5       5       true    0.47956458843779
6       6       true    0.37250860985059
7       7       true    0.3606193371519
8       8       true    0.35734223008066
9       9       true    0.47358258851812
---------------------
Errors: 0
Error rate: 0%

Odhady pro úroveň šumu (rozptyl) nastavený na hodnotu 5000:

0       2       false   0.27487188812131
1       2       false   0.28982103104273
2       2       true    0.41970028064418
3       3       true    0.4897877895855
4       4       true    0.42139011829457
5       5       true    0.43697124536669
6       5       false   0.2193472198283
7       2       false   0.34779616116913
8       3       false   0.24852067159185
9       9       true    0.33917120557291
---------------------
Errors: 5
Error rate: 50%

10. Síť naučená s malým počtem iterací a grafy odhadu sítě pro postupné zvyšování šumu

Opět si ukažme grafy, v nichž je vynesen odhad sítě (která číslice se na obrázku nachází) pro postupné zvyšování šumu. Tentokrát byla síť naučena velmi rychle, ovšem ukazuje se, že stále poměrně kvalitně:

Obrázek 15: Odhad pro číslici 0 pro střední hodnotu šumu od 0 do 1000.

Obrázek 16: Odhad pro číslici 1 pro střední hodnotu šumu od 0 do 1000.

Obrázek 17: Odhad pro číslici 2 pro střední hodnotu šumu od 0 do 1000.

Obrázek 18: Odhad pro číslici 3 pro střední hodnotu šumu od 0 do 1000.

Obrázek 19: Odhad pro číslici 4 pro střední hodnotu šumu od 0 do 1000.

Obrázek 20: Odhad pro číslici 5 pro střední hodnotu šumu od 0 do 1000.

Obrázek 21: Odhad pro číslici 6 pro střední hodnotu šumu od 0 do 1000.

Obrázek 22: Odhad pro číslici 7 pro střední hodnotu šumu od 0 do 1000.

Obrázek 23: Odhad pro číslici 8 pro střední hodnotu šumu od 0 do 1000.

Obrázek 24: Odhad pro číslici 9 pro střední hodnotu šumu od 0 do 1000.

11. Vliv roztřesení obrázků na odhad sítě

Mnohem větší vliv na schopnosti neuronové sítě rozeznat na validačních obrázcích číslice bude mít operace roztřesení. Ostatně podívejte se sami na následující obrázky a zjistíte, že u posledních dvou sekvencí již nemá neuronová síť ale ani člověk prakticky žádnou šanci rozeznat, jaké číslice se obrázku nacházely:

Obrázek 25: Jittering je nastaven na hodnotu 1.

Obrázek 26: Jittering je nastaven na hodnotu 2.

Obrázek 27: Jittering je nastaven na hodnotu 5.

Obrázek 28: Jittering je nastaven na hodnotu 10.

Obrázek 29: Jittering je nastaven na hodnotu 20.

To, že se síti rozpoznávání už tak dobře nedaří, můžeme vidět i z průběhu validace:

Jittering je nastaven na hodnotu 1:

0       0       true    0.35910954151032
1       1       true    0.57856174459343
2       2       true    0.40019361990113
3       3       true    0.54086705757525
4       4       true    0.58778335183714
5       5       true    0.27008819125063
6       6       true    0.35776762022894
7       7       true    0.4531367614001
8       4       false   0.21041576508232
9       9       true    0.3519680014153
---------------------
Errors: 1
Error rate: 10%

Jittering je nastaven na hodnotu 2:

0       0       true    0.38051842884868
1       1       true    0.38103882055846
2       2       true    0.39405629490146
3       3       true    0.45315678727988
4       4       true    0.50494134273053
5       3       false   0.19587720231237
6       6       true    0.23127709141202
7       7       true    0.31614212052061
8       9       false   0.2558102855285
9       9       true    0.32458093384014
---------------------
Errors: 2
Error rate: 20%

Jittering je nastaven na hodnotu 5:

0       4       false   0.28540120686069
1       1       true    0.30647200639512
2       2       true    0.34205264219205
3       3       true    0.34558813657259
4       4       true    0.34545742784285
5       4       false   0.16762162756383
6       9       false   0.21334603720808
7       9       false   0.23076108525153
8       3       false   0.20003963876897
9       0       false   0.28069596444979
---------------------
Errors: 6
Error rate: 60%

Jittering je nastaven na hodnotu 10:

0       0       true    0.22012625858352
1       1       true    0.32947438362054
2       2       true    0.25733056743774
3       2       false   0.2330541164283
4       4       true    0.30746443359153
5       4       false   0.1976126151968
6       3       false   0.23027809292622
7       9       false   0.23223999329789
8       2       false   0.23371376521436
9       3       false   0.22685291894002
---------------------
Errors: 6
Error rate: 60%

Jittering je nastaven na hodnotu 20:

0       0       true    0.19842203869463
1       2       false   0.25433237722833
2       3       false   0.24291030056728
3       3       true    0.21615011335859
4       4       true    0.21841478740651
5       2       false   0.17700056607258
6       2       false   0.27402457656592
7       9       false   0.23169880374807
8       3       false   0.22503740085146
9       3       false   0.19410856562881
---------------------
Errors: 7
Error rate: 70%

V posledních třech validačních krocích je odhad víceméně náhodný a navíc jsou pravděpodobnosti prakticky stejné.

12. Postupné zvyšování míry rozstřesení a odhad sítě vynesený do grafů

Ještě více jsou patrné problémy konvoluční neuronové sítě při klasifikaci číslic ve chvíli, kdy do grafu vyneseme odhad sítě pro rostoucí míru (úroveň) rozstřesení. Krásně odlišené pruhy, které jsme viděli v předchozích kapitolách, zde již nevidíme, pouze prakticky náhodný odhad sítě při vyšší míře rozstřesení (to je ovšem pochopitelné):

Obrázek 30: Odhad pro číslici 0 pro míru rozstřesení od 0 do 20.

Obrázek 31: Odhad pro číslici 1 pro míru rozstřesení od 0 do 20.

Obrázek 32: Odhad pro číslici 2 pro míru rozstřesení od 0 do 20.

Obrázek 33: Odhad pro číslici 3 pro míru rozstřesení od 0 do 20.

Obrázek 34: Odhad pro číslici 4 pro míru rozstřesení od 0 do 20.

Obrázek 35: Odhad pro číslici 5 pro míru rozstřesení od 0 do 20.

Obrázek 36: Odhad pro číslici 6 pro míru rozstřesení od 0 do 20.

Obrázek 37: Odhad pro číslici 7 pro míru rozstřesení od 0 do 20.

Obrázek 38: Odhad pro číslici 8 pro míru rozstřesení od 0 do 20.

MIF18 tip v článku Steiner

Obrázek 39: Odhad pro číslici 9 pro míru rozstřesení od 0 do 20.

13. Repositář s demonstračním příklady

Všechny demonstrační příklady, které jsme si popsali v předchozích kapitolách, najdete v GIT repositáři dostupném na adrese https://github.com/tisnik/torch-examples.git. Následují odkazy na zdrojové kódy jednotlivých příkladů:

14. Odkazy na Internetu

  1. THE MNIST DATABASE of handwritten digits
    http://yann.lecun.com/exdb/mnist/
  2. MNIST database (Wikipedia)
    https://en.wikipedia.org/wi­ki/MNIST_database
  3. MNIST For ML Beginners
    https://www.tensorflow.or­g/get_started/mnist/begin­ners
  4. Stránka projektu Torch
    http://torch.ch/
  5. Torch: Serialization
    https://github.com/torch/tor­ch7/blob/master/doc/seria­lization.md
  6. Torch: modul image
    https://github.com/torch/i­mage/blob/master/README.md
  7. Data pro neuronové sítě
    http://archive.ics.uci.edu/ml/in­dex.php
  8. LED Display Domain Data Set
    http://archive.ics.uci.edu/ml/da­tasets/LED+Display+Domain
  9. Torch na GitHubu (několik repositářů)
    https://github.com/torch
  10. Torch (machine learning), Wikipedia
    https://en.wikipedia.org/wi­ki/Torch_%28machine_learnin­g%29
  11. Torch Package Reference Manual
    https://github.com/torch/tor­ch7/blob/master/README.md
  12. Torch Cheatsheet
    https://github.com/torch/tor­ch7/wiki/Cheatsheet
  13. Neural network containres (Torch)
    https://github.com/torch/nn/blob/mas­ter/doc/containers.md
  14. Simple layers
    https://github.com/torch/nn/blob/mas­ter/doc/simple.md#nn.Line­ar
  15. Transfer Function Layers
    https://github.com/torch/nn/blob/mas­ter/doc/transfer.md#nn.tran­sfer.dok
  16. Feedforward neural network
    https://en.wikipedia.org/wi­ki/Feedforward_neural_net­work
  17. Biologické algoritmy (4) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-4-neuronove-site/
  18. Biologické algoritmy (5) – Neuronové sítě
    https://www.root.cz/clanky/biologicke-algoritmy-5-neuronove-site/
  19. Umělá neuronová síť (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Um%C4%9Bl%C3%A1_neuronov%C3%A1_s%C3%AD%C5%A5
  20. Učení s učitelem (Wikipedia)
    https://cs.wikipedia.org/wi­ki/U%C4%8Den%C3%AD_s_u%C4%8Di­telem
  21. Plotting with Torch7
    http://www.lighting-torch.com/2015/08/24/plotting-with-torch7/
  22. Plotting Package Manual with Gnuplot
    https://github.com/torch/gnu­plot/blob/master/README.md
  23. An Introduction to Tensors
    https://math.stackexchange­.com/questions/10282/an-introduction-to-tensors
  24. Gaussian filter
    https://en.wikipedia.org/wi­ki/Gaussian_filter
  25. Gaussian function
    https://en.wikipedia.org/wi­ki/Gaussian_function
  26. Laplacian/Laplacian of Gaussian
    http://homepages.inf.ed.ac­.uk/rbf/HIPR2/log.htm
  27. Odstranění šumu
    https://cs.wikipedia.org/wi­ki/Odstran%C4%9Bn%C3%AD_%C5%A­1umu
  28. Binary image
    https://en.wikipedia.org/wi­ki/Binary_image
  29. Erosion (morphology)
    https://en.wikipedia.org/wi­ki/Erosion_%28morphology%29
  30. Dilation (morphology)
    https://en.wikipedia.org/wi­ki/Dilation_%28morphology%29
  31. Mathematical morphology
    https://en.wikipedia.org/wi­ki/Mathematical_morphology
  32. Cvičení 10 – Morfologické operace
    http://midas.uamt.feec.vut­br.cz/ZVS/Exercise10/conten­t_cz.php
  33. Differences between a matrix and a tensor
    https://math.stackexchange­.com/questions/412423/dif­ferences-between-a-matrix-and-a-tensor
  34. Qualitatively, what is the difference between a matrix and a tensor?
    https://math.stackexchange­.com/questions/1444412/qu­alitatively-what-is-the-difference-between-a-matrix-and-a-tensor?
  35. BLAS (Basic Linear Algebra Subprograms)
    http://www.netlib.org/blas/
  36. Basic Linear Algebra Subprograms (Wikipedia)
    https://en.wikipedia.org/wi­ki/Basic_Linear_Algebra_Sub­programs
  37. Comparison of deep learning software
    https://en.wikipedia.org/wi­ki/Comparison_of_deep_lear­ning_software
  38. TensorFlow
    https://www.tensorflow.org/
  39. Caffe2 (A New Lightweight, Modular, and Scalable Deep Learning Framework)
    https://caffe2.ai/
  40. PyTorch
    http://pytorch.org/
  41. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  42. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/
  43. LuaJIT – Just in Time překladač pro programovací jazyk Lua (2)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/
  44. LuaJIT – Just in Time překladač pro programovací jazyk Lua (3)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-3/
  45. LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-4/
  46. LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-5-tabulky-a-pole/
  47. LuaJIT – Just in Time překladač pro programovací jazyk Lua (6 – překlad programových smyček do mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-6-preklad-programovych-smycek-do-mezijazyka-luajitu/
  48. LuaJIT – Just in Time překladač pro programovací jazyk Lua (7 – dokončení popisu mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-7-dokonceni-popisu-mezijazyka-luajitu/
  49. LuaJIT – Just in Time překladač pro programovací jazyk Lua (8 – základní vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-8-zakladni-vlastnosti-trasovaciho-jitu/
  50. LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-9-dalsi-vlastnosti-trasovaciho-jitu/
  51. LuaJIT – Just in Time překladač pro programovací jazyk Lua (10 – JIT překlad do nativního kódu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-10-jit-preklad-do-nativniho-kodu/
  52. LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-11-jit-preklad-do-nativniho-kodu-procesoru-s-architekturami-x86-a-arm/
  53. LuaJIT – Just in Time překladač pro programovací jazyk Lua (12 – překlad operací s reálnými čísly)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-12-preklad-operaci-s-realnymi-cisly/
  54. Lua Profiler (GitHub)
    https://github.com/luafor­ge/luaprofiler
  55. Lua Profiler (LuaForge)
    http://luaforge.net/projec­ts/luaprofiler/
  56. ctrace
    http://webserver2.tecgraf.puc-rio.br/~lhf/ftp/lua/
  57. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  58. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  59. lua2js
    https://www.npmjs.com/package/lua2js
  60. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  61. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  62. LuaJIT 2.0 SSA IR
    http://wiki.luajit.org/SSA-IR-2.0
  63. The LuaJIT Project
    http://luajit.org/index.html
  64. LuaJIT FAQ
    http://luajit.org/faq.html
  65. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  66. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  67. LuaJIT Wiki
    http://wiki.luajit.org/Home
  68. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  69. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  70. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  71. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  72. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  73. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  74. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  75. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  76. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  77. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
Našli jste v článku chybu?