Neural Network does not work the right way

Hi all,

I have recently been working on a C++ NeuralNetwork class and wanted to feed it with image data to be able to decide if the image data shows a cat or a dog.
So I got my training data from microsoft ( https://www.microsoft.com/en-us/download/details.aspx?id=54765 ).

For the jpg decompression I used the library stb_image.h ( https://github.com/nothings/stb/blob/master/stb_image.h ).
Then I went through the entire directory, decompressed each image, converted it to grayscale, and then fed the NeuralNetwork instance nn with the vector of this data and the destination 0 or 1, as implemented in the code below.
The problem is that if I then have it make a prediction on an image it has seen before, whether it is a cat or a dog, it predicts a 1. What am I doing wrong?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <iostream>
#include "NeuralN.h" // Neural Network class
#include "../myp/myprot.h" // just for the use of file_read() and file_size()
#include "../filesystem/filesystem.h" // filesystem::path::iterate_dir()
#include <fstream>
#include <algorithm>
#include <string>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"



using namespace std;

void greyscale_image_data(char unsigned *_Data, int _Image_Width, int _Image_Height, int _Channels)
{
    for (int i = 0; i < _Image_Width * _Image_Height; i++)
    {
        int offset = i * _Channels;

        unsigned char& red = _Data[offset + 0];
        unsigned char& green = _Data[offset + 1];
        unsigned char& blue = _Data[offset + 2];

        _Data[offset] = _Data[offset + 1] = _Data[offset + 2] = (red + green + blue) / 3;
    }
}

void iterate_n_images(std::vector<char const *>&& _Filenames, NeuralNetwork& nn, int n, double _Target)
{

    std::sort(_Filenames.begin(), _Filenames.end(), [](const char *fi, const char *f2)
    {

        return std::stoi(string(fi).substr(string(fi).find_last_of('\\') + 1 , string(fi).find_last_of('.'))) <
               std::stoi(string(f2).substr(string(f2).find_last_of('\\') + 1, string(f2).find_last_of('.')));
    });

    for (const char* filename : _Filenames)
    {

        if (filename == _Filenames.at(n))
            break;

        size_t jpeg_size = file_size(filename);

        char unsigned *jpeg_data = new unsigned char[jpeg_size];

        file_read(filename, jpeg_data, jpeg_size);

        int image_width, image_height, channels;
        unsigned char *image_data = stbi_load_from_memory( jpeg_data, jpeg_size, &image_width,
                                                           &image_height, &channels, STBI_rgb);

        delete[] jpeg_data;


        if (image_width != 500 || image_height != 375) // image in database has inappropriate scaling
            continue;

        greyscale_image_data(image_data, image_width, image_height, channels);
        nn.train({image_data, image_data + (image_width * image_height * STBI_rgb)}, {_Target});

    }
}



int main()
{
    std::vector <std::vector<double>> initWeights = {std::vector<double>(500*375*3, 0.5),
                                                     {.0, .0}};
    std::vector<double> initBiases = {std::vector<double>(500*375*3, 0)};
    double learningRate = 0.1;
    NeuralNetwork nn(initWeights, initBiases, learningRate);


    iterate_n_images(filesystem::path::iterate_dir("D:\\Nutzer\\cppprogr\\neuronal\\training_data\\Cat"), nn, 10, 0);
    iterate_n_images(filesystem::path::iterate_dir("D:\\Nutzer\\cppprogr\\neuronal\\training_data\\Dog"), nn, 10, 1);



    const char *filename = "D:\\Nutzer\\cppprogr\\neuronal\\training_data\\Cat\\0.jpg";

    size_t jpeg_size = file_size(filename);

    char unsigned *jpeg_data = new unsigned char[jpeg_size];

    file_read(filename, jpeg_data, jpeg_size);

    int image_width, image_height, channels;
    unsigned char *image_data = stbi_load_from_memory( jpeg_data, jpeg_size, &image_width,
                                                       &image_height, &channels, STBI_rgb);

    delete[] jpeg_data;

    
    greyscale_image_data(image_data, image_width, image_height, channels);


    // convert back: stbi_write_jpg("res.jpg", image_width, image_height, STBI_rgb, image_data, 100);

    std::cout << "Prediction: " << nn.feedforward({image_data, image_data + (image_width * image_height * STBI_rgb)})[0];




}


NeuralN.h:
https://pastebin.com/4bWvJAcA

Is there anyone out there that can help me?

Thanks in advance.

Luke
Last edited on
I'm not seeing matching function calls between what you show in your code and what you show in NeuralN.h.
Oh, it's calling the std::vector constructor that uses 2 iterators. Kind of a waste of a copy, but not an issue in logic. Nevermind.

One thing I would look at is if your biases/weights seem to be converging as the number of training iterations goes up, or if they seem to be diverging to some unexpected values.

Other note: This isn't the real issue, but note that averaging the red, green, and blue channels isn't the ideal way to form a grayscale image.
https://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale

Other note: You have duplicate code in your program. I'd suggest factoring out duplicate code into a function.
Last edited on
Thank you very much for this quick reply. I may have to make some changes to that.
Please note that my code at the moment is the sloppiest imaginable. i want to achieve the purpose first, then optimize.
Please note that my code at the moment is the sloppiest imaginable. i want to achieve the purpose first, then optimize.


Well, there's optimization and then there's optimization. Sloppy code is harder to get right in the first place, so refactoring into another function might actually save you time in debugging. Plus, if you fix a bug, you don't have to worry about fixing it in multiple places.

Optimizing performance is something different. That should wait until you have something working so you can see where the bottlenecks are and then streamline them.

But not touching obvious code structure issues (like refactoring into a function) until you get it working is a weak argument in my book.
Add some basic debugging info or run it through a debugger. For example, print the name of the file at line 54. That will help determine if you're actually passing it real file names. Does read_file() return an error?

When you say the program always predicts 1, Do you mean when you change line 87? Right now the code makes a prediction on just one file.
Whoever reported doug4 really doesn't like having someone tell very BASIC programming truths. Fie unto them!
Seconded...
Hey, this is a C++ forum, not a BASIC forum! :)
Last edited on
Registered users can post here. Sign in or register to post.