본문 바로가기
기타

MNIST NN 필기체 인식 with C( MNIST NN C code)

by ash9river 2024. 7. 4.

dataset

 

 

 

 

 

Train Layer (1 hidden Layer)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <time.h>

#define the_number_of_input_nodes 784
#define input_training_images 60000
#define output_testing_images 10000
#define zero_input_training_images 5923
#define one_input_training_images 6742
#define two_input_training_images 5958
#define three_input_training_images 6131
#define four_input_training_images 5842
#define batch_size 64
#define hidden_layer 3
#define first_hidden_nodes 100
#define second_hidden_nodes 100
#define third_hidden_nodes 100
#define output_nodes 10
#define Activation_Function sigmoid
#define uc unsigned char
#define epoches 400
#define learning_rate 0.0002

double sigmoid(double x);
double sigmoidMiboon(double x);

typedef struct Node {
    int numOfWeights;
    double* weights;
    double weightSum;
    double output;
    double bias;
    double delta;
    double loss;
}Node;

typedef struct NN {
    int numOfNodes;
    Node* node;
}NN;

typedef struct Image {
    uc* file;
}Image;

Image* loadImageData(int number, int idx);
Image* loadTestData(int number, int idx);

void makeNode(Node* node, int sizeOfInputNodes) {
    node->numOfWeights = sizeOfInputNodes;
    node->weights = (double*)malloc(sizeOfInputNodes * sizeof(double));
    srand(time(NULL));
    for (int i = 0; i < sizeOfInputNodes; ++i) {
        *(node->weights + i) = ((double)rand() / RAND_MAX) * 2.0 - 1.0;
        //printf("%lf\n", *(node->weights + i));
    }
    node->weightSum = 0.0;
    node->output = 0.0;
    node->bias = 0.01;
    node->loss = 0.0;
    node->delta = 0.0;
    return;
}

NN* makeNeuralNetwork(int sizeOfInputNodes, int sizeOfOutputNodes) {
    NN* nn = (NN*)malloc(sizeof(NN));
    nn->numOfNodes = sizeOfOutputNodes;
    nn->node = (Node*)malloc(nn->numOfNodes * sizeof(Node));
    for (int i = 0; i < nn->numOfNodes; ++i) {
        makeNode(nn->node + i, sizeOfInputNodes);
    }
    return nn;
}

void freeNN(NN* nn) {
    if (nn == NULL) return;
    for (int i = 0; i < nn->numOfNodes; ++i) {
        free((nn->node + i)->weights);
    }
    free(nn->node);
    free(nn);
}

void forpass(NN* inputNN, NN* outputNN) {
    //printf("%d ", outputNN->numOfNodes); 100
    //printf("%d ", inputNN->numOfNodes); 784
    //printf("%lf", *((outputNN->node + 99)->weights+100));
    //printf("%d", outputNN->node->numOfWeights);
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        (outputNN->node + i)->weightSum = 0.0;
        //printf("%lf ", (outputNN->node + i)->weightSum);
        //printf("%lf ", *((outputNN->node + i)->weights + 3));
        //printf("%lf ", (inputNN->node + 783)->output);
        //printf("%lf ", (inputNN->node + i)->output);

        // printf("%d ", (outputNN->node + i)->numOfWeights); ->100��

        for (int j = 0; j < inputNN->numOfNodes; ++j) {
            //printf("%lf ", *((outputNN->node + i)->weights + j));
            (outputNN->node + i)->weightSum += *((outputNN->node + i)->weights + j) * (inputNN->node + j)->output;
        }
        (outputNN->node + i)->weightSum += outputNN->node->bias;
    }
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        (outputNN->node + i)->output = Activation_Function((outputNN->node + i)->weightSum);
        //if (outputNN->numOfNodes == 100) printf("%d %lf\n", i, (outputNN->node + i)->output);
        //if(outputNN->numOfNodes==10) printf("%d %lf\n", i, (outputNN->node + i)->output);
    }
}

void backpropagationDirectly(NN* outputNN, NN* inputNN, int answer) {
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        double val = 0.0;
        if (answer == i) val = 1.0;
        (outputNN->node + i)->loss = val - (outputNN->node + i)->output;
        (outputNN->node + i)->delta = (outputNN->node + i)->loss * sigmoidMiboon((outputNN->node + i)->output);
        for (int j = 0; j < (outputNN->node + i)->numOfWeights; ++j) {
            *((outputNN->node + i)->weights + j) += learning_rate * (outputNN->node + i)->delta * (inputNN->node + j)->output;
            //printf("%d %lf\n", i, learning_rate * (outputNN->node + i)->delta * (inputNN->node + j)->output);
        }

    }
}
/*
void backpropagation(NN* outputNN, NN* inputNN,NN* restNN, int answer) {
    //printf("%d %d %d\n", outputNN->numOfNodes, inputNN->numOfNodes, restNN->numOfNodes);
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        (outputNN->node + i)->delta = 0.0;
        for (int j = 0; j < restNN->numOfNodes; ++j) {
            (outputNN->node + i)->delta += (restNN->node + j)->delta * *((restNN->node + j)->weights + i);
        }
        (outputNN->node + i)->delta *= sigmoidMiboon((outputNN->node + i)->output);

        for (int j = 0; j < (outputNN->node + i)->numOfWeights; ++j) {
        *((outputNN->node + i)->weights + j) -= learning_rate * (outputNN->node + i)->delta * (inputNN->node + j)->output;
        }
    }
}*/
void backpropagation(NN* outputNN, NN* inputNN, NN* restNN, int answer) {
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        double actual = (answer == i) ? 1.0 : 0.0; 
        double output = (outputNN->node + i)->output; 
        double delta = (actual - output) * sigmoidMiboon(output); 

        for (int j = 0; j < restNN->numOfNodes; ++j) {
            (restNN->node + j)->delta += delta * *((outputNN->node + i)->weights + j);
        }


        for (int j = 0; j < (outputNN->node + i)->numOfWeights; ++j) {
            *((outputNN->node + i)->weights + j) += learning_rate * delta * (inputNN->node + j)->output;
        }
    }
}

void makeInputNode(uc* file, NN* nn) {
    for (int i = 0; i < the_number_of_input_nodes; ++i) {
        (nn->node + i)->output = (double)*(file + i) / 255.0;
        //printf("%lf\n", (nn->node + i)->output);
//        if (i % 28 == 0) printf("\n");
//        if ((nn->node + i)->output > 0) printf("1");
//        else printf("0");
    }
}
void init(Image* data, int answer, NN* inputLayer, NN* firstLayer, NN* outputLayer) {
    for (int countOfBatch = 0; countOfBatch < batch_size; ++countOfBatch) {
        makeInputNode((data + countOfBatch)->file, inputLayer);
        forpass(inputLayer, firstLayer);
        //forpass(firstLayer, thirdLayer);
        forpass(firstLayer, outputLayer);

        //forpass(inputLayer, thirdLayer);
        //forpass(thirdLayer, outputLayer);

        backpropagationDirectly(outputLayer, firstLayer, answer);
        //backpropagation(thirdLayer, inputLayer, outputLayer, answer);
      //  backpropagation(thirdLayer, firstLayer, outputLayer, answer);
        //backpropagation(secondLayer, firstLayer, thirdLayer, answer);
        backpropagation(firstLayer, inputLayer, outputLayer, answer);
        //backpropagation(firstLayer, secondLayer, thirdLayer, answer);
        //backpropagation(inputLayer, firstLayer, secondLayer, answer);
    }
}

void freeImage(Image* data) {
    if (data == NULL) return;
    free(data);
}

int testing(Image* data, int answer, NN* inputLayer, NN* firstLayer, NN* outputLayer) {
    makeInputNode(data->file, inputLayer);
    forpass(inputLayer, firstLayer);
    //    forpass(firstLayer, thirdLayer);
        //forpass(secondLayer, thirdLayer);
        //forpass(inputLayer, thirdLayer);
    forpass(firstLayer, outputLayer);
    double maxVal = outputLayer->node->output;
    int maxIdx = 0;
    for (int i = 1; i < 10; ++i) {
        if (maxVal < (outputLayer->node + i)->output) {
            maxVal = (outputLayer->node + i)->output;
            maxIdx = i;
        }
    }
    //printf("%d %lf %d\n",answer, maxVal, maxIdx);
    if (maxIdx == answer) return 1;
    return 0;
}

void writeTestNote(int* correctAns, NN* inputLayer, NN* firstLayer, NN* outputLayer) {
    FILE* fp;
    fp = fopen("..\\mnist_raw\\hiddenLayerOne\\hiddenLayer1.txt", "wt");
    for (int i = 0; i < firstLayer->numOfNodes; ++i) {
        fprintf(fp, "node %d :", i);
        for (int j = 0; j < inputLayer->numOfNodes; ++j) {
            double tmp = *((firstLayer->node + i)->weights + j);
            fprintf(fp, "%lf ", tmp);
        }
        fprintf(fp, "\n");
    }
    fclose(fp);

    /*    fp = fopen(".\\hiddenLayer2.txt", "wt");
        for (int i = 0; i < secondLayer->numOfNodes; ++i) {
            fprintf(fp, "node %d :", i);
            for (int j = 0; j < firstLayer->numOfNodes; ++j) {
                double tmp = *((secondLayer->node + i)->weights+j);
                fprintf(fp, "%lf ", tmp);
            }
            fprintf(fp, "\n");
        }
        fclose(fp);*/

        /*fp = fopen(".\\hiddenLayer2.txt", "wt");
        for (int i = 0; i < thirdLayer->numOfNodes; ++i) {
            fprintf(fp, "node %d :", i);
            for (int j = 0; j < firstLayer->numOfNodes; ++j) {
                double tmp = *((thirdLayer->node +i)->weights+j);
                fprintf(fp, "%lf ", tmp);
            }
            fprintf(fp, "\n");
        }*
        fclose(fp);*/

    fp = fopen("..\\mnist_raw\\hiddenLayerOne\\outputLayer.txt", "wt");
    for (int i = 0; i < outputLayer->numOfNodes; ++i) {
        fprintf(fp, "node %d :", i);
        for (int j = 0; j < firstLayer->numOfNodes; ++j) {
            double tmp = *((outputLayer->node + i)->weights + j);
            fprintf(fp, "%lf ", tmp);
        }
        fprintf(fp, "\n");
    }
    fclose(fp);

    fp = fopen("..\\mnist_raw\\hiddenLayerThree\\ans.txt", "wt");
    int totalAns = 0;
    for (int i = 0; i < 10; ++i) {
        fprintf(fp, "correct of %d : ", i);
        int tmp = *(correctAns + i);
        fprintf(fp, "%d\n", tmp);
        totalAns += tmp;
    }
    double total = (double)totalAns / (double)output_testing_images;
    fprintf(fp, "total : %lf", total);
    fclose(fp);

    printf("real done!\n");
    printf("the accuracy is %lf", total);
}

int main() {
    // 1 epoches
    NN* inputLayer = makeNeuralNetwork(1, the_number_of_input_nodes);
    NN* firstLayer = makeNeuralNetwork(the_number_of_input_nodes, first_hidden_nodes);
    //NN* secondLayer = makeNeuralNetwork(first_hidden_nodes, second_hidden_nodes);
    //NN* thirdLayer = makeNeuralNetwork(second_hidden_nodes, third_hidden_nodes);
    //NN* thirdLayer = makeNeuralNetwork(the_number_of_input_nodes, third_hidden_nodes);
    NN* outputLayer = makeNeuralNetwork(third_hidden_nodes, output_nodes);

    for (int countEpoches = 0; countEpoches < epoches; ++countEpoches) {
        printf("%d epoche(s) start!\n", countEpoches + 1);
        for (int i = 0; i < 10; ++i) {
            printf("%d batch start:    ", i);
            for (int j = 0; j < 7000; j += batch_size) {
                printf("\b\b\b\b%4d", j / batch_size);
                Image* data = loadImageData(i, j);
                if (data == NULL) break;
                init(data, i, inputLayer, firstLayer, outputLayer);
                freeImage(data);
            }
            printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
            printf("\n");
        }
        printf("%d epoche(s) end! \n", countEpoches + 1);
    }

    int* correctAns = (int*)malloc(sizeof(int) * 10);
    printf("test start!\n");
    for (int i = 0; i < 10; ++i) {
        printf("%d test:     ", i);
        int tmp = 0;
        for (int j = 0; j < 1500; ++j) {
            printf("\b\b\b\b%4d", j);
            Image* data = loadTestData(i, j);
            if (data == NULL) break;
            tmp += testing(data, i, inputLayer, firstLayer, outputLayer);
            freeImage(data);
        }
        *(correctAns + i) = tmp;
        printf("\n%d test end!\n", i);
    }
    writeTestNote(correctAns, inputLayer, firstLayer, outputLayer);
    freeNN(inputLayer);
    freeNN(firstLayer);
    //freeNN(secondLayer);
    //freeNN(thirdLayer);
    freeNN(outputLayer);
}

char* makeFileName(int number, int idx) {
    int fileNameLength = 100;
    char* filename = (char*)malloc(fileNameLength * sizeof(char));
    if (filename == NULL) {
        printf("Memory allocation failed.\n");
        return NULL;
    }

    snprintf(filename, fileNameLength, "..\\mnist_raw\\training\\%d\\%d-%d.raw", number, number, idx);
    //    printf("%s\n", filename);
    return filename;
}

Image* loadTestData(int number, int idx) {
    Image* data = (Image*)malloc(sizeof(Image));
    data->file = (uc*)malloc(sizeof(uc) * the_number_of_input_nodes);
    int fileNameLength = 100;
    char* filename = (char*)malloc(fileNameLength * sizeof(char));
    if (filename == NULL) {
        printf("Memory allocation failed.\n");
        freeImage(data);
        return NULL;
    }
    snprintf(filename, fileNameLength, "..\\mnist_raw\\testing\\%d\\%d-%d.raw", number, number, idx);
    FILE* inputFile = fopen(filename, "rb");
    if (inputFile == NULL) {
        freeImage(data);
        free(filename);
        return NULL;
    }
    fread(data->file, sizeof(uc), the_number_of_input_nodes, inputFile);
    free(filename);
    fclose(inputFile);
    return data;
}

Image* loadImageData(int number, int idx) {
    Image* data = (Image*)malloc(sizeof(Image) * batch_size);
    for (int i = idx; i < idx + batch_size; ++i) {
        (data + i - idx)->file = (uc*)malloc(sizeof(uc) * the_number_of_input_nodes);
        char* filename = makeFileName(number, i);
        FILE* inputFile = fopen(filename, "rb");
        if (inputFile == NULL) {
            freeImage(data);
            free(filename);
            return NULL;
        }
        fread((data + i - idx)->file, sizeof(uc), the_number_of_input_nodes, inputFile);
        free(filename);
        fclose(inputFile);
    }
    return data;
}

double sigmoid(double x) {
    return 1 / (1 + exp(-x));
}

double sigmoidMiboon(double x) {
    double sig = sigmoid(x);
    return sig * (1 - sig);
}

Test Layer (1 hidden Layer)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <time.h>
#include <string.h>

#define the_number_of_input_nodes 784
#define input_training_images 60000
#define output_testing_images 10000
#define zero_input_training_images 5923
#define one_input_training_images 6742
#define two_input_training_images 5958
#define three_input_training_images 6131
#define four_input_training_images 5842
#define batch_size 64
#define hidden_layer 3
#define first_hidden_nodes 100
#define second_hidden_nodes 100
#define third_hidden_nodes 100
#define output_nodes 10
#define Activation_Function sigmoid
#define uc unsigned char
#define epoches 400
#define learning_rate 0.0002
#define line_max 7500
#define Activation_Function sigmoid

double sigmoid(double x);

typedef struct Node {
    int numOfWeights;
    double* weights;
    double weightSum;
    double output;
    double bias;
    double delta;
    double loss;
}Node;

typedef struct NN {
    int numOfNodes;
    Node* node;
}NN;

typedef struct Image {
    uc* file;
}Image;

Image* loadImageData(int number, int idx);
Image* loadTestData(int number, int idx);

void makeNode(Node* node, int sizeOfInputNodes) {
    node->numOfWeights = sizeOfInputNodes;
    node->weights = (double*)malloc(sizeOfInputNodes * sizeof(double));
    srand(time(NULL));
    for (int i = 0; i < sizeOfInputNodes; ++i) {
        *(node->weights + i) = ((double)rand() / RAND_MAX) * 2.0 - 1.0;
        //printf("%lf\n", *(node->weights + i));
    }
    node->weightSum = 0.0;
    node->output = 0.0;
    node->bias = 0.01;
    node->loss = 0.0;
    node->delta = 0.0;
    return;
}

NN* makeNeuralNetwork(int sizeOfInputNodes, int sizeOfOutputNodes) {
    NN* nn = (NN*)malloc(sizeof(NN));
    nn->numOfNodes = sizeOfOutputNodes;
    nn->node = (Node*)malloc(nn->numOfNodes * sizeof(Node));
    for (int i = 0; i < nn->numOfNodes; ++i) {
        makeNode(nn->node + i, sizeOfInputNodes);
    }
    return nn;
}

void makeInputNode(uc* file, NN* nn) {
    for (int i = 0; i < the_number_of_input_nodes; ++i) {
        (nn->node + i)->output = (double)*(file + i) / 255.0;
        //printf("%lf\n", (nn->node + i)->output);
//        if (i % 28 == 0) printf("\n");
//        if ((nn->node + i)->output > 0) printf("1");
//        else printf("0");
    }
}

void freeImage(Image* data) {
    if (data == NULL) return;
    free(data);
}

Image* loadTestData(int number, int idx) {
    Image* data = (Image*)malloc(sizeof(Image));
    data->file = (uc*)malloc(sizeof(uc) * the_number_of_input_nodes);
    int fileNameLength = 100;
    char* filename = (char*)malloc(fileNameLength * sizeof(char));
    if (filename == NULL) {
        printf("Memory allocation failed.\n");
        freeImage(data);
        return NULL;
    }
    snprintf(filename, fileNameLength, "..\\mnist_raw\\testing\\%d\\%d-%d.raw", number, number, idx);
    FILE* inputFile = fopen(filename, "rb");
    if (inputFile == NULL) {
        freeImage(data);
        free(filename);
        return NULL;
    }
    fread(data->file, sizeof(uc), the_number_of_input_nodes, inputFile);
    free(filename);
    fclose(inputFile);
    return data;
}

void forpass(NN* inputNN, NN* outputNN) {
    //printf("%d ", outputNN->numOfNodes); 100
    //printf("%d ", inputNN->numOfNodes); 784
    //printf("%lf", *((outputNN->node + 99)->weights+100));
    //printf("%d", outputNN->node->numOfWeights);
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        (outputNN->node + i)->weightSum = 0.0;
        //printf("%lf ", (outputNN->node + i)->weightSum);
        //printf("%lf ", *((outputNN->node + i)->weights + 3));
        //printf("%lf ", (inputNN->node + 783)->output);
        //printf("%lf ", (inputNN->node + i)->output);

        // printf("%d ", (outputNN->node + i)->numOfWeights); ->100��

        for (int j = 0; j < inputNN->numOfNodes; ++j) {
            //printf("%lf ", *((outputNN->node + i)->weights + j));
            (outputNN->node + i)->weightSum += *((outputNN->node + i)->weights + j) * (inputNN->node + j)->output;
        }
        (outputNN->node + i)->weightSum += outputNN->node->bias;
    }
    for (int i = 0; i < outputNN->numOfNodes; ++i) {
        (outputNN->node + i)->output = Activation_Function((outputNN->node + i)->weightSum);
        //if (outputNN->numOfNodes == 100) printf("%d %lf\n", i, (outputNN->node + i)->output);
        //if(outputNN->numOfNodes==10) printf("%d %lf\n", i, (outputNN->node + i)->output);
    }
}


int testing(Image* data, int answer, NN* inputLayer, NN* firstLayer, NN* outputLayer) {
    makeInputNode(data->file, inputLayer);
    forpass(inputLayer, firstLayer);
    //    forpass(firstLayer, thirdLayer);
        //forpass(secondLayer, thirdLayer);
        //forpass(inputLayer, thirdLayer);
    forpass(firstLayer, outputLayer);
    double maxVal = outputLayer->node->output;
    int maxIdx = 0;
    for (int i = 1; i < 10; ++i) {
        if (maxVal < (outputLayer->node + i)->output) {
            maxVal = (outputLayer->node + i)->output;
            maxIdx = i;
        }
    }
    //printf("%d %lf %d\n",answer, maxVal, maxIdx);
    if (maxIdx == answer) return 1;
    return 0;
}

void writeTestNote(NN* inputLayer, NN* firstLayer, NN* outputLayer) {
    FILE* fp;
    fp = fopen("..\\mnist_raw\\hiddenLayerOne\\hiddenLayer1.txt", "r");

    char line[line_max];

    for (int i = 0; i < first_hidden_nodes; ++i) {
        fgets(line, sizeof(line), fp);
        char* token = strtok(line, " ");
        int ptr = 1;
        //printf("%s\n", token);
        token = strtok(NULL, " ");
        token = strtok(NULL, " ");
        char* tmp = strchr(token, ':');
        if (tmp != NULL) {
            *tmp = ' '; // ':'를 공백으로 대체
        }
        *((firstLayer->node + i)->weights + 0) = atof(tmp);
        token = strtok(NULL, " ");
        while (token != NULL) {
            // 소수점 확인 후 출력
            double num = atof(token);
            *((firstLayer->node + i)->weights + ptr) = num;
            ++ptr;
            token = strtok(NULL, " ");
        }



    }

    fclose(fp);
    fp = fopen("..\\mnist_raw\\hiddenLayerOne\\outputLayer.txt", "r");

    for (int i = 0; i < output_nodes; ++i) {
        fgets(line, sizeof(line), fp);

        char* token = strtok(line, " ");
        int ptr = 1;

        token = strtok(NULL, " ");
        token = strtok(NULL, " ");
        char* tmp = strchr(token, ':');
        if (tmp != NULL) {
            *tmp = ' ';
        }
        *((outputLayer->node + i)->weights + 0) = atof(tmp);
        token = strtok(NULL, " ");
        while (token != NULL) {
            double num = atof(token);
            *((outputLayer->node + i)->weights + ptr) = num;
            ++ptr;
            token = strtok(NULL, " ");
        }
    }

    fclose(fp);
}
int main() {
    NN* inputLayer = makeNeuralNetwork(1, the_number_of_input_nodes);
    NN* firstLayer = makeNeuralNetwork(the_number_of_input_nodes, first_hidden_nodes);
    //NN* secondLayer = makeNeuralNetwork(first_hidden_nodes, second_hidden_nodes);
    //NN* thirdLayer = makeNeuralNetwork(second_hidden_nodes, third_hidden_nodes);
    //NN* thirdLayer = makeNeuralNetwork(the_number_of_input_nodes, third_hidden_nodes);
    NN* outputLayer = makeNeuralNetwork(first_hidden_nodes, output_nodes);
    int* correctAns = (int*)malloc(sizeof(int) * 10);
    writeTestNote(inputLayer, firstLayer, outputLayer);
    printf("test start!\n");
    for (int i = 0; i < 10; ++i) {
        printf("%d test:     ", i);
        int tmp = 0;
        for (int j = 0; j < 1500; ++j) {
            printf("\b\b\b\b%4d", j);
            Image* data = loadTestData(i, j);
            if (data == NULL) break;
            tmp += testing(data, i, inputLayer, firstLayer, outputLayer);
            freeImage(data);
        }
        *(correctAns + i) = tmp;
        printf("\n%d test end!\n", i);
    }
    int totalAns = 0;
    for (int i = 0; i < 10; ++i) {
        totalAns += *(correctAns + i);
    }
    printf("the accuray is %lf!", (double)totalAns / (double)100);
}

double sigmoid(double x) {
    return 1 / (1 + exp(-x));
}

 

결과

 

 

 

 

 

돌리는데 1시간 넘게 걸림