Skip to content

Commit 654e64a

Browse files
committed
fix float opts, add readme
1 parent 617cbc4 commit 654e64a

File tree

6 files changed

+46
-10
lines changed

6 files changed

+46
-10
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,4 @@ modules.order
5050
Module.symvers
5151
Mkfile.old
5252
dkms.conf
53-
*.png
5453
.vscode/

Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
CC=clang
1+
CC=gcc
22
FILES=src/*.c
33

4-
WARNS=-std=c2x -Wall -Wextra -Wpedantic -Wshadow -Wformat=2 -Werror=return-type
4+
WARNS=-std=c23 -Wall -Wextra -Wpedantic -Wshadow -Wformat=2 -Werror=return-type -Wno-unknown-pragmas
55
ifdef debug
66
CFLAGS=-lm -g3 $(sanitize:%=-fsanitize=%)
77
else
8-
CFLAGS=-lm -DNDEBUG -O3 -march=native -flto -ffast-math -fno-reciprocal-math
8+
CFLAGS=-lm -DNDEBUG -O3 -march=native -flto -ffast-math
99
endif
1010

1111
default:

README.md

+30-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,30 @@
1-
# mnist-c
1+
# mnist-c
2+
C implementation of a neural network for classification.
3+
![model classifying "5" with 99.8% confidence](img/demo.png)
4+
5+
## Performance
6+
Using default settings training takes about 1.5s per epoch on my Ryzen 5 2600.
7+
- **[MNIST Handwritten Digits dataset](http://yann.lecun.com/exdb/mnist/)**: ~97% accuracy
8+
- **[Zalando Fashion-MNIST dataset](https://github.com/zalandoresearch/fashion-mnist)**: ~85% accuracy
9+
10+
## Architecture
11+
Three fully connected layers (64-32-10 by default) with sigmoid activations in between, though this is trivially modifiable.
12+
13+
## Usage
14+
### Training
15+
```sh
16+
git clone https://github.com/atzuur/mnist-c
17+
cd mnist-c
18+
make # optionally add `CC=clang EXTRA="-fuse-ld=lld"` to use clang
19+
./a.exe train
20+
```
21+
### Prediction
22+
1. Draw a 28x28 MNIST-like image (see [demo](img/demo.png))
23+
2. Convert to raw floating point using e.g. [FFmpeg](https://ffmpeg.org):
24+
```sh
25+
ffmpeg -v error -i YOUR_IMAGE.png -pix_fmt grayf32 -f rawvideo YOUR_IMAGE
26+
```
27+
3. Run prediction using the model:
28+
```sh
29+
./a.exe YOUR_IMAGE
30+
```

img/demo.png

60.1 KB
Loading

src/main.c

+6-4
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ static int test_mnist(network* nn, const char* image) {
5858
}
5959

6060
float* image_data = malloc(nn->layer_sizes[0] * sizeof *image_data);
61-
if (fread(image_data, sizeof *image_data, MNIST_SAMPLE_LEN, image_file) !=
62-
(size_t)MNIST_SAMPLE_LEN) {
61+
int64_t n_read = fread(image_data, sizeof *image_data, MNIST_SAMPLE_LEN, image_file);
62+
if (n_read != MNIST_SAMPLE_LEN) {
6363
free(image_data);
64+
printf("expected %" PRId64 " bytes in image, got %" PRId64 "\n",
65+
MNIST_SAMPLE_LEN * sizeof *image_data, n_read * sizeof *image_data);
6466
perror("fread");
6567
return 1;
6668
}
@@ -91,12 +93,12 @@ int main(int argc, const char** argv) {
9193
}
9294

9395
network nn;
94-
int64_t layer_sizes[] = {MNIST_SAMPLE_LEN, 50, 40, 10};
96+
int64_t layer_sizes[] = {MNIST_SAMPLE_LEN, 64, 32, 10};
9597
nn_init(&nn, 4, layer_sizes);
9698

9799
int ret;
98100
if (strcmp(argv[1], "train") == 0) {
99-
ret = train_mnist(&nn, 30, 10, 3.0f);
101+
ret = train_mnist(&nn, 30, 8, 3.0f);
100102
} else {
101103
ret = test_mnist(&nn, argv[1]);
102104
}

src/network.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
#include <string.h>
1111
#include <time.h>
1212

13-
static float sigmoid(float x) {
13+
#pragma float_control(precise, on, push)
14+
#if defined(__GNUC__) && !defined(__clang__)
15+
__attribute__((optimize("no-finite-math-only")))
16+
#endif
17+
static float
18+
sigmoid(float x) {
1419
return 1.0f / (expf(-x) + 1);
1520
}
21+
#pragma float_control(pop)
1622

1723
static float d_sigmoid(float x) {
1824
return sigmoid(x) * (1.0f - sigmoid(x));

0 commit comments

Comments
 (0)