Deep Learning Model Deployment
- tutorial: [1]
shape = [batch,height,width,channel]
constant:1
2
3
4tf.zeros(shape)
tf.constant(0.4, shape)
tf.random_norm(shape, stddev=1e-1)
tf.truncated_norm(shape, stddev=1e-1)
variable:1
2
3
4
5tf.get_variable(varname, shape=[5,5,64,192], initializer=XXX)
initializer=tf.zeros_initializer() # bias
initializer=tf.random_normal_initializer(0.0, 0.02)
initializer=tf.truncated_normal_initializer(0.0, 0.02)
initializer=tf.contrib.layers.xavier_initializer() # weight
Empirically, for weight initialization, xavier is better than truncated_norm, better than random_normal.
1 | tf.summary.scalar('total_loss', total_loss) #scalar, histogram, etc |
after running the code, open tensorboard tensorboard --logdir='./train'
1 | # save all the variables |
inspect the checkpoint file1
2
3
4
5
6
7from tensorflow.python import pywrap_tensorflow
checkpoint_path = os.path.join(model_dir, "model.ckpt")
reader = pywrap_tensorflow.NewCheckpointReader(checkpoint_path)
var_to_shape_map = reader.get_variable_to_shape_map()
for key in var_to_shape_map:
print("tensor_name: ", key)
print(reader.get_tensor(key)) # Remove this is you want to print only variable names
convolutional layer
1 | kernel = tf.Variable(tf.zeros([5,5,64,192])) |
dropout layer
1 | keep_prob = tf.placeholder(tf.float32) |
pooling layer
1 | tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID') |
loss layer
1 | tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)) |
metric layer
1 | # calculate top-k accuracy |
activation layer
1 | tf.nn.relu(prev_layer) |
other layers
1 | tf.one_hot(self.input_class, self.n_class) |
1 | tf.matmul |
training stage
1 | optimizer = tf.train.GradientDescentOptimizer(0.01) |
testing stage
1 | acc.eval(feed_dict={x:x_test, y:y_test}) |
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
sess.run(tf.initialize_local_variables())
#main code
coord.request_stop()
coord.join(threads)
1 | * With tfrecord and slim, using queue is much easier by using `slim.queues.QueueRunners` |
sv.managed_session 1 | sv = tf.train.Supervisor(logdir=logdir, save_summaries_secs=0, saver=None) |
check tensorflow version
1 | tf.__version__.split('.')[0] != "1" |
count parameter numbers
1 | parameter_count = tf.reduce_sum([tf.reduce_prod(tf.shape(v)) for v in tf.trainable_variables()]) |
Input raw data (e.g., text, image address). There are many different implementations of queue.
feature-label pair: [enqueue-op] [fetch-func] [slice-input-producer] [string-input-producer]
image-image pair: [slice-input-producer][string-input-producer]
A more elegant way is converting raw data to tfrecord format.
Tips:
setting large number_of_threading (e.g., 10) is helpful.
shuffle the training samples to avoid homogenuity when necessary.
place the training data in local disk instead of removable disk (consider I/O speed).
All the GPU memory will be notoriously filled up even if you designate one GPU device.
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
config = tf.ConfigProto()
config.gpu_options.allow_growth=True
sess = tf.Session(config=config)
os.environ[“CUDA_VISIBLE_DEVICES”] = “0,1” # use python to set environment variables
One typical to use mulitple GPU is to average gradients, please refer to the sample code.
slim = tf.contrib.slim
for certain functions, assign default values to certain parameters
with slim.arg_scope([func1, func2, ....], arg1=val1, arg2=val2, ....)
https://www.tensorflow.org/api_docs/python/tf/contrib/layers
class Block(collections.namedtuple(‘Block’, [‘scope’, ‘unit_fn’, ‘args’])):
net = slim.utils.collect_named_outputs(outputs_collections, sc.name, net)
Note that initialization function must be associated with sess.1
2init_fn = slim.assign_from_checkpoint_fn(checkpoint_path, slim.get_variables_to_restore())
init_fn(sess)
With slim:1
2
3
4
5
6
7
8
9batch_norm_params = {
# Decay for the moving averages.
'decay': batch_norm_decay,
# epsilon to prevent 0s in variance.
'epsilon': batch_norm_epsilon,
# collection containing update_ops.
'updates_collections': tf.GraphKeys.UPDATE_OPS,
}
slim.arg_scope([slim.conv2d], normalizer_fn=slim.batch_norm, normalizer_params=normalizer_params)
For tf.contrib.layers or tf.slim, when is_training=True, mean and variance based on each batch are used and moving_mean and moving_variance are updated if applicable. When is_training=False, loaded moving-mean an moving_variance are used.
To launch the update of moving_mean and moving_variance, special attention needs to be paid because this update operation is detached from gradient descent, which can be realized in the following ways.
The first method:1
2
3
4
5
6
7
8update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
variables_to_train = _get_variables_to_train()
grads_and_vars = optimizer.compute_gradients(total_loss, variables_to_train)
grad_updates = optimizer.apply_gradients(grads_and_vars)
update_ops.append(grad_updates)
update_op = tf.group(*update_ops)
with tf.control_dependencies([update_op]):
train_op = tf.identity(total_loss)
The second method:1
2
3update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
train_op = optimizer.minimize(loss)
The third method:1
train_op = slim.learning.create_train_op(total_loss, optimizer)
Otherwise, one can set updates_collections=None in slim.batch_norm to force the updates in place, but that can have a speed penalty, especially in distributed settings.
However, when trained on small-scale datasets, using moving_mean and moving_variance in the test stage often leads to extremely poor performance (close to random guess). This is due to the code start which renders moving_mean/variance unstable. There are two ways to fix the cold-start issue:
in the testing stage, also set is_training=True, i.e., use the mean and variance based on each test batch.
decrease batch_norm running average decay from default 0.999 to something like 0.99, which can speed up the start-up. When tuning decay, there is a trade-off between warm-up speed and statistical accuracy. For small-scale datasets, warm-up may take exceedingly long time, e.g., 300 epochs.
without slim: tf.nn.batch_normalization, no moving_mean/variance1
2
3
4
5
6
7
8
9
10
11def batchnorm(bn_input):
with tf.variable_scope("batchnorm"):
# this block looks like it has 3 inputs on the graph unless we do this
bn_input = tf.identity(bn_input)
channels = bn_input.get_shape()[3]
offset = tf.get_variable("offset", [channels], dtype=tf.float32, initializer=tf.zeros_initializer())
scale = tf.get_variable("scale", [channels], dtype=tf.float32, initializer=tf.random_normal_initializer(1.0, 0.02))
mean, variance = tf.nn.moments(bn_input, axes=[0, 1, 2], keep_dims=False)
normalized = tf.nn.batch_normalization(bn_input, mean, variance, offset, scale, variance_epsilon=1e-5)
return normalized
print all model variables1
2
3
4
5tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES) # all the global variables
slim.get_model_variables() or tf.get_collection(tf.GraphKeys.MODEL_VARIABLES)
#variables defined by slim (tf.contrib.framework.model_variable)
#excluding gradient variables
tf.trainable_variables() #excluding graident variables and batch_norm variables (moving_mean and moving_variance)
print regularization losses(weight decay) and other losses1
2
3slim.losses.get_regularization_losses()
slim.losses.get_losses() # losses except weight decay
slim.losses.get_total_loss(add_regularization_losses=False)
Framework Code
MyApp(False)means noredirect while MyApp(True, 'output.txt') will redirect the output to the file output.txt.
Derivation from wx.App uses def OnInit(self) while others use def __init__(self)
Donnot forget return True
Simplest Frame: frame.Show(), frame.Centre()
A frame with menubar, toolbar, and statusbar: UpdateUIEvents are sent periodically by the framework during idle time to allow the application to check if the state of a control needs to be updated.
Append a menu_item with icon,
qmi = wx.MenuItem(fileMenu, APP_EXIT, '&Quit\tCtrl+Q')
qmi.SetBitmap(wx.Bitmap('exit.png'))
fileMenu.AppendItem(qmi)
Menu check_item
self.shtl = viewMenu.Append(wx.ID_ANY, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)
self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.shst)
def ToggleStatusBar(self, e):
if self.shst.IsChecked():
self.statusbar.Show()
else:
self.statusbar.Hide()
Pop a Dialogue box with validator: derivation from wx.PyValidator
A login dialogue before main frame: wx.PyValidator

Splash Screen: splash before entering main frame wx.SplashScreen

SplitPanel (hide, show): a horizontally or vertically splited panel, loading html file





Component Code
Bitmap: use bitmap to beautify the appearance, or use the bitmap brush (transparent widgets may be a little troublesome here)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnPaint(self, event):
dc = wx.PaintDC(self)
dc.SetBackgroundMode(wx.TRANSPARENT)
brush1 = wx.BrushFromBitmap(wx.Bitmap('pattern1.jpg'))
dc.SetBrush(brush1)
w, h = self.GetSize()
dc.DrawRectangle(0, 0, w, h)
Simplest Button: use lib to build advanced buttons
self.Bind(wx.EVT_BUTTON, self.OnButton, button)
def OnButton(self, event):GetChildren(), GetParent(), GetId(), FindWindowById(self.btnId)Advanced button: bitmap button, toggle button, gradient button
Frame Icon: personalize the frame icon
con = wx.Icon(path, wx.BITMAP_TYPE_PNG)
self.SetIcon(icon):
Interaction with Clipboard: paste to and copy from system clipboard
Drop Files to Frame: : wx.PyDropTarget
Help in Frame: when initializing Frame
pre = wx.PreFrame()
pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
pre.Create(parent, *args, **kwargs)
self.PostCreate(pre)
Checkbox: use more general event detector to simplify code
`self.Bind(wx.EVT_CHECKBOX, self.OnCheck)`
e_obj = event.GetEventObject()
MessageBox
def ShowMessage(self,event):
wx.MessageBox('Download completed', 'Info', wx.OK | wx.ICON_INFORMATION)
Open file_dialogue
dlg = wx.FileDialog(self, "Open File", style=wx.FD_OPEN)
if dlg.ShowModal() == wx.ID_OK:
fname = dlg.GetPath()
handle = open(fname, 'r')
self.txtctrl.SetValue(handle.read())
handle.close()
popup menu: right click to pop up the menu
static box: a static box containing components
list ctrl: multi-column list

customtree: a tree-structure file browser

Styled Text: i.e., python-style text
About info: info including name, version, copyright, and description
Choose Color Dialogue
colour_data = wx.ColourData()
colour = self.GetBackgroundColour()
colour_data.SetColour(colour)
colour_data.SetChooseFull(True)
dlg = wx.ColourDialog(self, colour_data)
if dlg.ShowModal() == wx.ID_OK:
colour = dlg.GetColourData().GetColour()
self.SetBackgroundColour(colour)
self.Refresh()
dlg.Destroy()
Timer
self._timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self._timer)
self._timer.Start(100)
self._timer.Stop()
Execute Command Line
import outputwin
self.output = outputwin.OutputWindow(self)
self.output.StartProcess("ping %s" % url, blocksize=64)
Video Player: First, you need to install MplayerCtrl lib. Secondly, place the mplayer folder under the current working directory.
Layout
wx.BoxSizer: proportion is used to control main direction and wx.EXPAND is used to control the other direction. Note in BoxSizer, alignment is only valid in one direction. AddSpacer(50) is equal to Add((50,50)). AddStretchSpacer() is equal to Add((0,0),proportion=1).
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.AddSpacer(50)
sizer.Add(sth,proportion=0, flag=wx.ALL, border=5) #use flag to mark which side has border
sizer.Add((-1,10)) #add a black space, height=10
# sizer.Add(sth,proportion=0, wx.EXPAND|wx.RIGHT|wx.ALIGN_RIGHT, border=5)
sizer.AddSpacer((0,0)) #sizer.AddStretchSpacer()
self.SetSizer(sizer)
self.SetInitialSize()
wx.GridSizer: proportion is usually set as 0, use Add((20,20), 1, wx.EXPAND) to take up space.
wx.GridSizer(2, 2, vgap=0, hgap=0)
msizer.Add(sth, 0, wx.EXPAND)
wx.FlexGridSizer: make some rows and columns growable.
fgs.AddGrowableRow(2)
fgs.AddGrowableCol(1)
wx.GridBagSizer: use pos and span to indicate the location and size.
sizer = wx.GridBagSizer(vgap=8, hgap=8)
sizer.Add(sth, (1, 2), (1, 15), wx.EXPAND)
Notes
Event Propagation: When an event can intrigue multiple events, use event.skip() to guaranttee the occurrence of following events. Take keyevents.py for an example.
Virtual Ride: wx.PyPannel
Bind function which will be checked in the idle timeself.Bind(wx.EVT_UPDATE_UI, self.OnUpdateEditMenu)
check type
1 | isinstance(n,int) |
list
1 | t1 = ['a', 'b', 'c']; |
dictionary
1 | eng2sp = {'one': 'uno', 'two': 'dos', 'three': 'tres'} |
tupe: similar with list but immutable
file operation
1 | fout = os.getcwd() |
Open SMTP service for your email and obtain the SMTP password.
Create a Python script
1 | import win32serviceutil |
Make python file into an exe
1 | pyinstaller -F MyService.py |
Make exe into a Windows service
1 | sc create MyServer binPath=$exe_path |