Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__
weights
venv
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ production ready to use in real device. It can be easily extended to be used wit
different neural net structure.

## Requirements
Python 3.5, Tensorflow 1.4.0, Keras 2.1.3
Python 3.10, libraries listed in requirements.txt

## Installation
Open powershell in your working directory and paste these commands:
```
git clone https://github.com/ZFTurbo/Verilog-Generator-of-Neural-Net-Digit-Detector-for-FPGA.git
cd Verilog-Generator-of-Neural-Net-Digit-Detector-for-FPGA
python3.10 -m venv venv
./venv/Scripts/Activate.ps1
pip3.10 install -r requirements.txt
```

## How to run:
* python r01_train_neural_net_and_prepare_initial_weights.py
Expand Down
16 changes: 8 additions & 8 deletions a00_common_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def show_resized_image(P, w=1000, h=1000):

def load_mnist_data(type='channel_last'):
from keras.datasets import mnist
from keras.utils import np_utils

from keras.utils import to_categorical
# input image dimensions
nb_classes = 10
img_rows, img_cols = 28, 28
Expand All @@ -51,8 +51,8 @@ def load_mnist_data(type='channel_last'):
print(X_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)
Y_train = to_categorical(y_train, nb_classes)
Y_test = to_categorical(y_test, nb_classes)

return X_train, Y_train, X_test, Y_test

Expand All @@ -69,11 +69,11 @@ def save_history(history, path, columns=('loss', 'val_loss')):

def prepare_image_from_camera(im_path):
img = cv2.imread(im_path)
print('Read image: {} Shape: {}'.format(im_path, img.shape))
# print('Read image: {} Shape: {}'.format(im_path, img.shape))

# Take central part of image with size 224х224
img = img[8:-8, 48:-48]
print('Reduced shape: {}'.format(img.shape))
# print('Reduced shape: {}'.format(img.shape))

# Convert to grayscale with human based formulae https://samarthbhargav.wordpress.com/2014/05/05/image-processing-with-python-rgb-to-grayscale-conversion/
# Divider here is 16 for easier implementation of division in verilog for FPGA.
Expand All @@ -95,8 +95,8 @@ def prepare_image_from_camera(im_path):
# Check dynamic range
min_pixel = output_image.min()
max_pixel = output_image.max()
print('Min pixel: {}'.format(min_pixel))
print('Max pixel: {}'.format(max_pixel))
# print('Min pixel: {}'.format(min_pixel))
# print('Max pixel: {}'.format(max_pixel))

# Rescale dynamic range if needed (no Verilog implementation, so skip)
if 0:
Expand Down
36 changes: 18 additions & 18 deletions r01_train_neural_net_and_prepare_initial_weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
import random


epochs = 20
optimizer = 'Adam'
learning_rate = 0.003

gpu_use = 0
os.environ["KERAS_BACKEND"] = "tensorflow"
os.environ["CUDA_VISIBLE_DEVICES"] = "{}".format(gpu_use)
Expand Down Expand Up @@ -178,12 +182,10 @@ def evaluate_generator_train(X_test, Y_test, batch_size):
Y_train = np.concatenate((Y_train, np.zeros((Y_train.shape[0], 1))), axis=1)
Y_test = np.concatenate((Y_test, np.zeros((Y_test.shape[0], 1))), axis=1)

optimizer = 'Adam'
learning_rate = 0.001
if optimizer == 'SGD':
optim = SGD(lr=learning_rate, decay=1e-6, momentum=0.9, nesterov=True)
optim = SGD(learning_rate=learning_rate, decay=1e-6, momentum=0.9, nesterov=True)
else:
optim = Adam(lr=learning_rate)
optim = Adam(learning_rate=learning_rate)
model.compile(optimizer=optim, loss='categorical_crossentropy', metrics=['accuracy'])
if continue_training == 1:
print('Continue training. Loading weights from: {}'.format(cache_model_path))
Expand All @@ -197,19 +199,17 @@ def evaluate_generator_train(X_test, Y_test, batch_size):
ModelCheckpoint(cache_model_path, monitor='val_loss', save_best_only=True, verbose=0),
]

history = model.fit_generator(generator=batch_generator_train(X_train, Y_train, batch_size, 'train'),
epochs=2000,
steps_per_epoch=200,
validation_data=batch_generator_train(X_test, Y_test, batch_size, 'valid'),
validation_steps=200,
verbose=2,
max_queue_size=20,
callbacks=callbacks)

save_history(history, final_model_path, columns=('acc', 'val_acc'))
score = model.evaluate_generator(generator=evaluate_generator_train(X_test, Y_test, batch_size, 'valid'),
steps=X_test.shape[0] // batch_size,
max_queue_size=1)
history = model.fit(batch_generator_train(X_train, Y_train, batch_size, 'train'),
epochs=epochs,
steps_per_epoch=200,
validation_data=batch_generator_train(X_test, Y_test, batch_size, 'valid'),
validation_steps=200,
verbose=2,
callbacks=callbacks)

save_history(history, final_model_path, columns=('accuracy', 'val_accuracy'))
score = model.evaluate(evaluate_generator_train(X_test, Y_test, batch_size),
steps=X_test.shape[0] // batch_size)
print('Test score:', score[0])
print('Test accuracy:', score[1])
model.load_weights(cache_model_path)
Expand All @@ -235,5 +235,5 @@ def evaluate_generator_train(X_test, Y_test, batch_size):
print('Accuracy: {:.2f}%'.format(100*accuracy / len(pred_answers)))

'''
Accuracy: 97.39%
Accuracy after 2000 epochs: 97.39%
'''
26 changes: 13 additions & 13 deletions r05_verilog_generator_neural_net_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,8 +1254,8 @@ def TOP(directory, size, razmer, max_address_value, output_neurons_count, max_we
for i in range(len(layers)):
layer = layers[i]
if 'Conv2D' in str(type(layer)):
mem = layer.output_shape[3]-1
filt = layer.input_shape[3]-1
mem = layer.output.shape[3]-1
filt = layer.input.shape[3]-1

if (start == 0): file.write(" if ((TOPlvl=="+str(TOPlvl)+")&&(step=="+str(step)+")) nextstep = 1;\n")
else: start = 0
Expand All @@ -1269,7 +1269,7 @@ def TOP(directory, size, razmer, max_address_value, output_neurons_count, max_we
file.write(" conv_en = 1;\n")
file.write(" mem = "+str(mem)+";\n")
file.write(" filt = "+str(filt)+";\n")
file.write(" matrix = "+str(layer.input_shape[1])+";\n")
file.write(" matrix = "+str(layer.input.shape[1])+";\n")
if 'GlobalMaxPooling2D' in str(type(layers[i+2])):
file.write(" globmaxp_en = 1;\n")
else: file.write(" globmaxp_en = 0;\n")
Expand All @@ -1282,7 +1282,7 @@ def TOP(directory, size, razmer, max_address_value, output_neurons_count, max_we
TOPlvl += 1
elif 'MaxPooling2D' in str(type(layer)):
if not 'GlobalMaxPooling2D' in str(type(layer)):
for i in range(int(layer.input_shape[3]/4)):
for i in range(int(layer.input.shape[3]/4)):
file.write(" if ((TOPlvl=="+str(TOPlvl)+")&&(STOP_maxp==0))\n")
file.write(" begin\n")
file.write(" memstartp = "+str(one)+"+"+str(i)+"*matrix2*((4 >> (num_conv >> 1)));\n")
Expand All @@ -1301,8 +1301,8 @@ def TOP(directory, size, razmer, max_address_value, output_neurons_count, max_we
file.write(" begin \n")
file.write(" globmaxp_en = 0; \n")
file.write(" nextstep = 1; \n")
file.write(" in_dense = "+str(layer.input_shape[1])+"; \n")
file.write(" out_dense = "+str(layer.output_shape[1])+"; \n")
file.write(" in_dense = "+str(layer.input.shape[1])+"; \n")
file.write(" out_dense = "+str(layer.output.shape[1])+"; \n")
file.write(" end \n")

step += 2
Expand Down Expand Up @@ -1541,9 +1541,9 @@ def convert_to_fix_point(arr1, bit):
layer = model.layers[i]
if 'Conv2D' in str(type(layer)):
total_conv_layers_number += 1
conv_inputs.append(layer.input_shape[1])
conv_mem.append(layer.input_shape[3])
conv_filt.append(layer.output_shape[3])
conv_inputs.append(layer.input.shape[1])
conv_mem.append(layer.input.shape[3])
conv_filt.append(layer.output.shape[3])
w = layer.get_weights()
conv_block_size_1 = len(w[0])
conv_block_size_2 = len(w[0][0])
Expand All @@ -1553,18 +1553,18 @@ def convert_to_fix_point(arr1, bit):
max_weights_per_layer = max_weights_per_layer_1
elif 'MaxPooling2D' in str(type(layer)):
if not 'GlobalMaxPooling2D' in str(type(layer)):
total_maxp_layers_number += int(layer.input_shape[3]/4)
total_maxp_layers_number += int(layer.input.shape[3]/4)
elif 'Dense' in str(type(layer)):
w = layer.get_weights()
total_dense_layers_number += 1
dense_inputs.append(layer.input_shape[1])
dense_outputs.append(layer.output_shape[1])
dense_inputs.append(layer.input.shape[1])
dense_outputs.append(layer.output.shape[1])
max_address_value += len(layer.get_weights()[0][0]) * len(w[0])
max_weights_per_layer_1 = int(len(w[0][0]) * len(w[0])/(conv_block_size_1*conv_block_size_2)) + 1
if max_weights_per_layer_1 > max_weights_per_layer:
max_weights_per_layer = max_weights_per_layer_1
if i == len(model.layers) - 1:
output_neurons_count = layer.output_shape[1]
output_neurons_count = layer.output.shape[1]

max_input_image_size = max(conv_inputs)
steps_count = 2 + (total_conv_layers_number*2) + (total_dense_layers_number*2) + 1
Expand Down
Binary file added requirements.txt
Binary file not shown.
Binary file removed weights/keras_model_low_weights_digit_detector.h5
Binary file not shown.
113 changes: 0 additions & 113 deletions weights/keras_model_low_weights_digit_detector.h5.csv

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file removed weights/optimal_bit.pklz
Binary file not shown.