Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds SeparableConv2D support #11

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ a VGG16 convolutional neural network and all kind of networks based on the layer
Currently the following layers are supported:

* Convolution2D with padding valid/same and strides
* SeparableConvolution2D with padding valid/same and strides
* MaxPooling2D with strides
* Dense
* Softmax
Expand Down
5 changes: 5 additions & 0 deletions applications/daimler/compiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ def test_daimler(self):
generate("db", {"x": 18, "y": 36}, "img.db")
train("img.db", "model.h5")
compile("model.h5", ".", "img.db")

def test_daimler_separable(self):
generate("db", {"x": 18, "y": 36}, "img.db")
train("img.db", "model_separable.h5", use_separable=True)
compile("model_separable.h5", ".", "img.db")

if __name__ == "__main__":
t = Tests()
Expand Down
58 changes: 30 additions & 28 deletions applications/daimler/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,41 @@
print("PlaidML not found, using Tensorflow")
USE_PLAIDML = False
import argparse
from keras.layers import Flatten, MaxPooling2D, Convolution2D, Dropout, Dense
from keras.layers import Flatten, MaxPooling2D, Convolution2D, SeparableConvolution2D, Dropout, Dense
from keras.models import Sequential
from applications.daimler.loader import load_imdb

def train(imgdb_path, model_path):
def create_conv_model(shape, ConvolutionType=Convolution2D):
conv_model = Sequential()
conv_model.add(ConvolutionType(8, (3, 3), input_shape=shape,
activation='relu', padding='same'))
conv_model.add(MaxPooling2D(pool_size=(2, 2)))
conv_model.add(ConvolutionType(24, (3, 3), padding='same', activation='relu'))
conv_model.add(MaxPooling2D(pool_size=(2, 2)))
conv_model.add(ConvolutionType(48, (4, 3), padding='same', activation='relu'))
conv_model.add(MaxPooling2D(pool_size=(2, 2)))
conv_model.add(Dropout(0.4))
conv_model.add(Flatten())
conv_model.add(Dense(2, activation='softmax'))
return conv_model

def train(imgdb_path, model_path, use_separable=False):
imdb = load_imdb(imgdb_path)
x = imdb['images']
y = imdb['y']

usual_model = Sequential()
usual_model.add(Convolution2D(4, (3, 3), input_shape=(x.shape[1], x.shape[2], 1),
activation='relu', padding='same'))
usual_model.add(MaxPooling2D(pool_size=(2, 2)))
usual_model.add(Convolution2D(16, (3, 3), padding='same', activation='relu'))
usual_model.add(MaxPooling2D(pool_size=(2, 2)))
usual_model.add(Convolution2D(32, (3, 3), padding='same', activation='relu',))
usual_model.add(MaxPooling2D(pool_size=(4, 2)))
usual_model.add(Dropout(0.4))
usual_model.add(Convolution2D(2, (2, 2), activation='softmax'))
usual_model.add(Flatten())

dense_model = Sequential()
dense_model.add(Convolution2D(4, (3, 3), input_shape=(x.shape[1], x.shape[2], 1),
activation='relu', padding='same'))
dense_model.add(MaxPooling2D(pool_size=(2, 2)))
dense_model.add(Convolution2D(16, (3, 3), padding='same', activation='relu'))
dense_model.add(MaxPooling2D(pool_size=(2, 2)))
dense_model.add(Convolution2D(32, (3, 3), padding='same', activation='relu'))
dense_model.add(MaxPooling2D(pool_size=(2, 2)))
dense_model.add(Dropout(0.4))
dense_model.add(Flatten())
dense_model.add(Dense(2, activation='softmax'))
input_shape = (x.shape[1], x.shape[2], 1)
epochs = 30 if use_separable else 10

# Select the current model here
model = dense_model
if use_separable:
model = create_conv_model(input_shape, SeparableConvolution2D)
else:
model = create_conv_model(input_shape, Convolution2D)

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())
model.fit(x, y, batch_size=1000, epochs=10, verbose=1, validation_split=0.05)
model.fit(x, y, batch_size=1000, epochs=epochs, verbose=1, validation_split=0.05)
model.save(model_path)

if __name__ == "__main__":
Expand All @@ -61,17 +57,23 @@ def train(imgdb_path, model_path):
'Default is img.db in current folder.')
parser.add_argument('-m', '--model-path', dest='model_path',
help='Store the trained model using this path. Default is model.h5.')
parser.add_argument('-s', '--separable', dest='use_separable', action='store_true',
help='Whether to use separable convolution instead of regular ones. Default is regular (Conv2D).')

args = parser.parse_args()

imgdb_path = "img.db"
model_path = "model.h5"
use_separable = False

if args.imgdb_path is not None:
imgdb_path = args.imgdb_path

if args.model_path is not None:
model_path = args.model_path

train(imgdb_path, model_path)
if args.use_separable is not None:
use_separable = args.use_separable

train(imgdb_path, model_path, use_separable)

21 changes: 19 additions & 2 deletions nncg/nncg.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from tensorflow.keras import backend as K
from tensorflow.keras.layers import Convolution2D, MaxPooling2D, Flatten, \
from tensorflow.keras.layers import SeparableConvolution2D, Convolution2D, MaxPooling2D, Flatten, \
Dropout, BatchNormalization, LeakyReLU, InputLayer, Dense
import keras.layers as kl

Expand Down Expand Up @@ -82,7 +82,9 @@ def keras_compile(self, imdb, model, code_path, identifier=None, image_mean=0, a
# Read the Keras model layer by layer and add it to the graph

for i, layer in enumerate(model.layers):
if type(layer) == Convolution2D or type(layer) == kl.convolutional.Conv2D:
if type(layer) == SeparableConvolution2D or type(layer) == kl.convolutional.SeparableConv2D:
cur_node = self.add_separable_conv2d(layer, cur_node)
elif type(layer) == Convolution2D or type(layer) == kl.convolutional.Conv2D:
cur_node = self.add_conv2d(layer, cur_node)
elif type(layer) == MaxPooling2D or type(layer) == kl.pooling.MaxPooling2D:
cur_node = self.add_maxpool2d(layer, cur_node)
Expand Down Expand Up @@ -339,6 +341,21 @@ def add_conv2d(self, layer: Convolution2D, prev_node) -> Node:
cur_node = self.add_test_node(cur_node, layer)
return cur_node

def add_separable_conv2d(self, layer, prev_node):
w1 = K.eval(layer.weights[0])
w2 = K.eval(layer.weights[1])
b = K.eval(layer.bias)
strides = layer.strides
padding = layer.padding
activation = layer.activation
cur_node = SeparableConv2D_Depthwise_Node(w1, np.zeros(b.shape, dtype='float32'), strides, padding, prev_node)
cur_node = SeparableConv2D_Pointwise_Node(w2, b, (1, 1), 'valid', cur_node)
cur_node = self.add_activation(activation, cur_node)
if self.testing != 0:
cur_node = self.add_test_node(cur_node, layer)
return cur_node


def write_c(self, path):
"""
Write the global graph as C code.
Expand Down
Loading