TensorFlow Example 1*
MNIST Model Compilation Workflow*
MNIST is an entry-level computer vision dataset. Its input is handwritten digit images with a resolution of 28x28 pixels, and its output is the probability of the image corresponding to digits 0-9. Below, using the mnist model (TensorFlow v1.15) included with TensorFlow as an example, we illustrate the usage of the gxnpuc toolchain and API.
1. Generating NPU Files*
The MNIST computation model in this example is very simple and can be represented by a single formula: y = x * W + b (During training, softmax is also computed, but since we only need the index of the maximum value in the result during inference, and softmax is a monotonically increasing function, omitting this function does not affect the result). Here, x is the input data, y is the output data, and W and b are the trained parameters. The training process involves continuously adjusting W and b based on the computed y and the expected y_. On the NPU, we only need the trained W and b, not the training process.
1.1 Generating CKPT and PB Files*
First, we need to generate CKPT and PB files. Additionally, to conveniently specify the model's input and output nodes during NPU compilation, we can assign names to the input and output nodes.
See the highlighted parts of the main function for details.
def main(_):
# Import data
mnist = input_data.read_data_sets(FLAGS.data_dir)
# Create the model
x = tf.placeholder(tf.float32, [None, 784], name="input_x") # Specify input name as input_x for easy use in compilation config scripts
w = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
y = tf.matmul(x, w) + b
y = tf.identity(name="result") # Specify output name as result for easy use in compilation config scripts
# Define loss and optimizer
y_ = tf.placeholder(tf.int64, [None])
# The raw formulation of cross-entropy,
#
# tf.reduce_mean(-tf.reduce_sum(y_ * tf.math.log(tf.nn.softmax(y)),
# reduction_indices=[1]))
#
# can be numerically unstable.
#
# So here we use tf.compat.v1.losses.sparse_softmax_cross_entropy on the raw
# logit outputs of 'y', and then average across the batch.
cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y)
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
config = tf.ConfigProto()
jit_level = 0
if FLAGS.xla:
# Turns on XLA JIT compilation.
jit_level = tf.OptimizerOptions.ON_1
config.graph_options.optimizer_options.global_jit_level = jit_level
run_metadata = tf.RunMetadata()
sess = tf.compat.v1.Session(config=config)
tf.global_variables_initializer().run(session=sess)
# Train
train_loops = 1000
for i in range(train_loops):
batch_xs, batch_ys = mnist.train.next_batch(100)
# Create a timeline for the last loop and export to json to view with
# chrome://tracing/.
if i == train_loops - 1:
sess.run(train_step,
feed_dict={x: batch_xs,
y_: batch_ys},
options=tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE),
run_metadata=run_metadata)
trace = timeline.Timeline(step_stats=run_metadata.step_stats)
with open('/tmp/timeline.ctf.json', 'w') as trace_file:
trace_file.write(trace.generate_chrome_trace_format())
else:
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
# Test trained model
correct_prediction = tf.equal(tf.argmax(y, 1), y_)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(sess.run(accuracy,
feed_dict={x: mnist.test.images,
y_: mnist.test.labels}))
# Generate CKPT and PB files
saver = tf.train.Saver()
saver.save(sess, "./mnist.ckpt")
tf.train.write_graph(sess.graph_def, "./", "mnist.pb")
sess.close()
After running the program, the mnist.ckpt.* and mnist.pb files will be generated in the current directory.
Note
Be sure to remember the model's input, output op names and their shapes, as they will be needed in the yaml configuration file for the gxnpuc compilation tool later.
1.2 Merging CKPT and PB Files into a FROZEN_PB File*
Use the freeze_graph.py script to merge mnist.ckpt.* and mnist.pb into a single pb file.
Note
The freeze_graph.py script may differ across TensorFlow versions.
Execute the command:
$ python freeze_graph.py --input_graph=mnist.pb --input_checkpoint=./mnist.ckpt --output_graph=mnist_with_ckpt.pb --output_node_names=result
mnist_with_ckpt.pb file.
Here, --input_graph is followed by the input PB name, --input_checkpoint is followed by the input CKPT name, --output_graph is followed by the merged FROZEN_PB file name, and --output_node_names is followed by the output node names (separated by commas if multiple).
After execution, the mnist_with_ckpt.pb file is generated in the current directory.
If the model is saved using the saved_model format, use the following command to generate the FROZEN_PB file:
$ python freeze_graph.py --input_saved_model_dir=./saved_model_dir --output_graph=mnist_with_ckpt.pb --output_node_names=result
1.3 Editing the NPU Configuration File*
Edit the configuration file mnist_config.yaml. The meanings are explained in the comments.
CORENAME: APUS # Chip model
MODEL_FILE: mnist_with_ckpt.pb # Input PB file
IN_FEATS_FILE: feats.txt # Input feature values, should cover model application scenarios as much as possible
QUANT_FILE: quant.yaml # Output quantization file
OUTPUT_FILE: mnist.h # Output NPU file name
COMPRESS: true # Compress fully connected layer weights
OUTPUT_TYPE: c_code # NPU file type
INPUT_OPS:
input_x: [1, 784] # Input node name and data dimensions; each run inputs 1x784 data, i.e., one image
OUTPUT_OPS: [result] # Output node name
FUSE_BN: true # Merge BN parameters into convolution (if applicable)
MAX_CACHE_SIZE: 0 # Allocate CACHE memory for storing weights and data
USE_DATA_CACHE: false # Data is not stored in CACHE
Note
Here, input_x, result correspond one-to-one with the model's input, output op names.
1.4 Compilation*
First, use the gxnpuc tool to compile and generate the quantization file:
$ gxnpuc mnist_config.yaml -q
mnist.h:
$ gxnpuc mnist_config.yaml
------------------------
Memory allocation info:
Mem1(data): 40
Mem2(instruction): 100
Mem3(in): 1568
Mem4(out): 20
Mem5(cache): 0
Mem6(weights): 15712
Total NPU Size (Mem0+Mem1+Mem2+Mem5+Mem6): 15852
Total Memory Size: 17440
The memory regions are described as follows:
| Memory Region | Description |
|---|---|
| Mem1(data) | Intermediate data memory |
| Mem2(instruction) | Instruction memory |
| Mem3(in) | Input data memory |
| Mem4(out) | Output data memory |
| Mem5(cache) | Weights and data memory in SRAM |
| Mem6(weights) | Weights memory |
2. Executing NPU Files*
After the NPU file is generated, the model needs to be deployed and run on the GX830X development board.