Jump to content

Machine learning demo


Josh
 Share

Recommended Posts

Inspired by the image generation AI technologies that are going around, I took an hour and wrote the first machine learning algorithm I've ever tried. I don't know anything about how these are implemented by other people, I just did what seemed intuitive to me. The routine uses only one image for training, and the goal is just to output the same image. Scoring is based on the total differences between the original and outputted images. Of course, you would want to test this routine on multiple images to ensure the input even has anything to do with the output at all!

Here is the input image:

bricksx34.png.94c27a22df222943a263193e405ee7eb.png

And here is what my algorithm creates:

output.jpg.f1316bd0d3f5f091c03c10bb2f54ddc5.jpg

As you can see, it is not very intelligent. :D You can see from the printed output the scores do in fact get better with time, so it is learning...slowly. (Lower numbers are better.) This should really give you an idea of just how brute-force these training algorithms are!

This is a working machine learning example in a very small amount of code. Maybe someone will experiment with this and improve the results? Since the routine can be trained with any set of input and output images, you could train it to generate normal maps, upsample an image, or anything else you want. 

Here is the code:

#include "UltraEngine.h"

using namespace UltraEngine;

struct TrainingSet
{
    std::vector<unsigned char> input;
    std::vector<unsigned char> goal;
};

struct Neuron
{
    std::vector<int> outputs;
    int level;
    int firingthreshold;
    int sign;

    Neuron() : sign(1), level(0), firingthreshold(0)
    {}
};

struct Brain
{
    std::vector<std::vector<Neuron> > layers;
    float mutationrate;
    int maxsynapses;

    void Reset()
    {
        for (int n = 0; n < layers.size(); ++n)
        {
            for (int k = 0; k < layers[n].size(); ++k)
            {
                layers[n][k].level = 127;
            }
        }
    }

    Brain() {}

    Brain(const int inputs, const int outputs, const int width, const int levels, const int maxsynapses)
    {
        mutationrate = 0;
        this->maxsynapses = maxsynapses;
        Reset();
        layers.resize(levels);
        layers[0].resize(inputs);
        layers[layers.size() - 1].resize(outputs);
        for (int n = 0; n < layers.size() - 1; ++n)
        {
            if (n != 0) layers[n].resize(width);
            for (int k = 0; k < layers[n].size(); ++k)
            {
                if ((k % 2) == 1) layers[n][k].sign = -1;
                layers[n][k].outputs.resize(maxsynapses);
            }
        }
        Mutate(1.0f);
    }

    void Mutate(const float strength)
    {
        mutationrate = strength;
        int n, k;
        for (n = 0; n < layers.size() - 1; ++n)
        {
            for (k = 0; k < layers[n].size(); ++k)
            {
                for (int m = 0; m < layers[n][k].outputs.size(); ++m)
                {
                    if (Random(0.0f, 1.0f) < strength)
                    {
                        layers[n][k].outputs[m] = Max(-1, Round(Random(-1, int(layers[n + 1].size() - 1))));
                    }
                }
            }
        }
    }

    float Execute(const std::vector<uint32_t>& inputs, const std::vector<uint32_t>& outputs)
    {
        Reset();
        const auto& first = layers[0];
        const auto& last = layers[layers.size() - 1];
        Assert(inputs.size() == first.size());
        Assert(outputs.size() == last.size());
        int n, k, m;

        //Set inputs 
        for (k = 0; k < layers[0].size() - 1; ++k)
        {
            layers[0][k].level = inputs[k];
        }

        //Think
        for (n = 0; n < layers.size() - 1; ++n)
        {
            for (k = 0; k < layers[n].size(); ++k)
            {
                //if (Abs(layers[n][k].level) >= layers[n][k].firingthreshold)
                //{
                    for (m = 0; m < layers[n][k].outputs.size(); ++m)
                    {
                        int nindex = layers[n][k].outputs[m];
                        if (nindex == -1) continue;
                        layers[n + 1][nindex].level += layers[n][k].level * layers[n][k].sign;
                        //layers[n + 1][nindex].level = Clamp(layers[n + 1][nindex].level, 0, 255);
                    }
               // }
            }
        }

        //Evaluate results
        float err = 0;
        for (k = 0; k < layers[layers.size() - 1].size() - 1; ++k)
        {
            err += Abs(layers[layers.size()-1][k].level - outputs[k]);
        }

        return err;
    }
};

int main(int argc, const char* argv[])
{
    const int generations = 100;
    const int population = 2;
    const int brainlayers = 10;
    const int maxsynapses = 4;
    const float maxmutationrate = 0.3f;
    const int imagesize = 64;
    const int brainlayerwidth = imagesize * imagesize * 3;
    const int randomseed = 100;

    Seed(randomseed);

    auto plg = LoadPlugin("Plugins/FITextureLoader.dll");

    int sz = imagesize;
    auto brain = Brain(sz*sz*3*256, sz*sz*3, sz*sz*3*4, brainlayers, maxsynapses);
    auto pixmap = LoadPixmap("https://opengameart.org/sites/default/files/bricksx34.png");
    pixmap = pixmap->Resize(sz, sz);

    std::vector<uint32_t> goal(sz * sz * 3);

    std::vector<uint32_t> inputs(sz * sz * 3 * 256);
    std::vector<uint32_t> outputs(sz * sz * 3);
    std::fill(inputs.begin(), inputs.end(), 0);

    for (int x = 0; x < pixmap->size.x; ++x)
    {
        for (int y = 0; y < pixmap->size.y; ++y)
        {
            auto rgba = pixmap->ReadPixel(x,y);
            int r = Red(rgba);
            int g = Green(rgba);
            int b = Blue(rgba);
            for (int c = 0; c < r; ++c)
            {
                inputs[(x * pixmap->size.y * 3 + y * 3 + 0) * 256 + c] = 1;
            }
            for (int c = 0; c < g; ++c)
            {
                inputs[(x * pixmap->size.y * 3 + y * 3 + 1) * 256 + c] = 1;
            }
            for (int c = 0; c < b; ++c)
            {
                inputs[(x * pixmap->size.y * 3 + y * 3 + 2) * 256 + c] = 1;
            }
            outputs[x * pixmap->size.y * 3 + y * 3 + 0] = r;
            outputs[x * pixmap->size.y * 3 + y * 3 + 1] = g;
            outputs[x * pixmap->size.y * 3 + y * 3 + 2] = b;
        }
    }
   
    std::vector<Brain> mutations(population);
    int bestbrain = 0;
    float bestscore = 0;
    float bestmutationrate = 0;
    float avgbestmutationrate = 0.5f;
    float prevbestscore = 0;

    for (int i = 0; i < generations; ++i)
    {
        for (int n = 0; n < mutations.size(); ++n)
        {
            mutations[n] = brain;            
            mutations[n].mutationrate = 0;
            if (n > 0)
            {
                mutations[n].Mutate(Random(0.0f, maxmutationrate));
            }
        }

        bestbrain = 0;
        bestscore = 0;

        for (int n = 0; n < mutations.size(); ++n)
        {
            float score;
            if (i > 0 and n == 0)
            {
                score = prevbestscore;
            }
            else
            {
                score = mutations[n].Execute(inputs, outputs);
            }
            if (n == 0)
            {
                bestscore = score;
                bestmutationrate = mutations[n].mutationrate;
            }
            else
            {
                if (score <= bestscore)
                {
                    bestscore = score;
                    bestbrain = n;
                    bestmutationrate = mutations[n].mutationrate;
                }
            }
        }

        if (prevbestscore != bestscore)
        {
            Print(String(i) + ": " + String(bestscore));
        }

        if (bestbrain != 0) brain = mutations[bestbrain];
        prevbestscore = bestscore;
        avgbestmutationrate = avgbestmutationrate * 0.5 + bestmutationrate * 0.5;
        avgbestmutationrate = Clamp(avgbestmutationrate, 0.0001f, 1.0f);
        //Print(avgbestmutationrate);
    }

    for (int x = 0; x < pixmap->size.x; ++x)
    {
        for (int y = 0; y < pixmap->size.y; ++y)
        {
            int r = brain.layers[brain.layers.size()-1][x * pixmap->size.y * 3 + y * 3 + 0].level;
            int g = brain.layers[brain.layers.size()-1][x * pixmap->size.y * 3 + y * 3 + 1].level;
            int b = brain.layers[brain.layers.size()-1][x * pixmap->size.y * 3 + y * 3 + 2].level;
            pixmap->WritePixel(x, y, RGBA(r,g,b,255));
        }
    }

    pixmap->Save("output.jpg");
    RunFile("output.jpg");
}

 

  • Like 2

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

  • Josh changed the title to Machine learning demo

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...