Josh Posted September 9, 2022 Share Posted September 9, 2022 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: And here is what my algorithm creates: As you can see, it is not very intelligent. 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"); } 2 Quote 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 More sharing options...
Recommended Posts
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.