2016年6月21日 星期二

[Tensorflow]Loading a tensorflow graph with the C++ API by using Mnist



"Tensorflow is an open source software library for numerical computation using data flow graphs. "
Tensorflow provides python API and C++ API. However, the document about loading a graph with C++ API is few. In some case, we need a C++ level api to run tensorflow.
Thanks to Jim Fleming write a complete loading graph example (link), saving my plenty of times.

I am based on Jim Fleming's article and add the mnist example to show
   - how to write datas into input tensors
   - how to read datas from output tensors
   - what need to be care when we initial input and output tensors

Requirement

  -  Install tensorflow from source code ,
     "GET STARTED" --> "installing from source"
  - Install bazel

Source code can be checked on my github repository
https://github.com/JackyTung/tensorgraph

Create Graph

source code : gengraph/

import tensorflow as tf
import time
from tensorflow.examples.tutorials.mnist import input_data
# === prepare tensorflow network === #

#config setting
imageDim = 784
outputDim = 10

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# None means that a dimension can be any of any length
x = tf.placeholder(tf.float32, [None, imageDim], name="input")
# 784-dimensional image vectors by it to produce 10-dimensional vectors
W = tf.Variable(tf.zeros([imageDim, outputDim]), dtype=tf.float32, name="Weight")
# a shape of [10]
b = tf.Variable(tf.zeros([outputDim]), dtype=tf.float32, name="bias")
# softmax
y = tf.nn.softmax(tf.matmul(x, W)+b, name="softmax")

....

# Add ops to save and restore all the variables
saver = tf.train.Saver()

with tf.Session() as sess:
 sess.run(tf.initialize_all_variables())
#Training
    for i in range(1000):
        if i % 100 == 0:
            print "iteration num :", i
        batch_xs, batch_ys = mnist.train.next_batch(100)
        sess.run(train_step,feed_dict={x: batch_xs, y_: batch_ys})

    # Save checkpoint, graph.pb and tensorboard
    saver.save(sess, "models/model.ckpt") 
    tf.train.write_graph(sess.graph.as_graph_def(), "models/", "graph.pb")
    tf.train.SummaryWriter("board", sess.graph)
#Testing
.....    

when we create graph, need to care about
1. good naming on tensor.
2. the dimension of the input tensor and output tensor
(in this case, input tensor is x, output tensor is y)

Creating a simple library

source code : loadgraph/

If we create our own library, create a new folder like this : tensorflow/tensorflow/<my project name>.
In this case, I am going to call the project loadgraph.
In the folder loadgraph/, I create a file name mnist.cc (because it is a mnist example).

In mnist.cc, we do the following things:
1. Initialize a TensorFlow session.
2. Read in the graph we exported above.
3. Add the graph to the session.
4. Setup our inputs and outputs.
5. Write datas into input tensors
6. Run the graph, populating the outputs.
7. Read value from the outputs tensors.
8. Do the mnist prediction

Some steps is shown in Jim's article, in here, I show some different from his article.

cout << "preparing input data..." << endl;
  // config setting
  int imageDim = 784;
  int nTests = 10000;
  
  // Setup inputs and outputs:
  Tensor x(DT_FLOAT, TensorShape({nTests, imageDim}));

  MNIST mnist = MNIST("./MNIST_data/");
  auto dst = x.flat<float>().data();
  for (int i = 0; i < nTests; i++) {
    auto img = mnist.testData.at(i).pixelData;
    std::copy_n(img.begin(), imageDim, dst);
    dst += imageDim;
  }

  cout << "data is ready" << endl;
  vector<pair<string, Tensor>> inputs = {
    { "input", x}
  };

  // The session will initialize the outputs
  vector<Tensor> outputs;
  // Run the session, evaluating our "softmax" operation from the graph
  status = session->Run(inputs, {"softmax"}, {}, &outputs);
  if (!status.ok()) {
    cout << status.ToString() << "\n";
    return 1;
  }else{
   cout << "Success load graph !! " << "\n";
  }


The input tensor and output tensor dimension should be the same as mnist.py.
Besides, the name of input tensor {"input", x} and output tensor {"softmax"} should also be same as mnist.py.

// start compute the accuracy,
  // arg_max is to record which index is the largest value after 
  // computing softmax, and if arg_max is equal to testData.label,
  // means predict correct.
  int nHits = 0;
  for (vector<Tensor>::iterator it = outputs.begin() ; it != outputs.end(); ++it) {
   auto items = it->shaped<float, 2>({nTests, 10}); // 10 represent number of class
 for(int i = 0 ; i < nTests ; i++){
      int arg_max = 0;
            float val_max = items(i, 0);
            for (int j = 0; j < 10; j++) {
         if (items(i, j) > val_max) {
               arg_max = j;
               val_max = items(i, j);
                }
      }
      if (arg_max == mnist.testData.at(i).label) {
          nHits++;
            } 
 }
  }
  float accuracy = (float)nHits/nTests;
  cout << "accuracy is : " << accuracy << ", and Done!!" << 

Figure out the output dimension, in this case, the output dimension is (nTest, 10).
nTest: represent test numbers
10 : represent number of classes.
we use iterator it as a pointer to read the data from output tensor.

Now, we create BUILD file for our project, our src file contain mnist.cc and MNIST.h.
(MNIST.h is a mnist data loader.)


cc_binary(
    name = "mnistpredict",
    srcs = ["mnist.cc", "MNIST.h"],
    deps = [
        "//tensorflow/core:tensorflow",
    ],
);

Here is the final directory structure:
- tensorflow/tensorflow/loadgraph
- tensorflow/tensorflow/loadgraph/mnist.cc
- tensorflow/tensorflow/loadgraph/MNIST.h
- tensorflow/tensorflow/loadgraph/BUILD

Compile and Run

- From inside folder, run bazel build :mnistpredict
- From the repository root, go into bazel-bin/tensorflow/loadgraph
- Copy frozen_graph.pb and Mnist_data/ to loadgraph/
- run ./mnistpredict and check the output is the same as mnist.py or not

Note and Following tutorial

1. The build binary is 154MB, I think it is still a huge size if we want to put our model to applications.
I will introduce what is going on in the build file "//tensorflow/core:tensorflow".
Choose the library we need, and then will reduce our binary size.

2. In loadgraph, we can notice that I use "frozen_graph.pb".
the frozen_graph.pb is combined from graph.pb and model.ckpt.
I will explain the reason why I want to combine these two files.

3.  We use the mnist for our example in this article. I think it is a simple example in the machine learning. When we use more complicate example, we need to know network clearly, especially what are the input tensors and what are the output tensors. Because some redundant input tensors will be dropped out after freeze graph. If we do not know our network clearly, we may confused why input tensor is disappear.


Be honestly, I am a new beginner in machine learning.
If I wrote something wrong or have a better suggestion, leave message to me.
I'll revise and update my article :)

Check my next tutorial: Try to downsize graph.pb model

2016年6月15日 星期三

About Me


Jacky Tung (Kuan-Chieh Tung)
Work as : Software Engineer in HTC
Email address: jackytung8085@gmail.com


PROFILE
- Jacky Tung received master degree in Information Systems and Application from National Tsing Hua University, Hsinchu, Taiwan.
- He is currently a software engineer in HTC.
- His current job is
   1. Scalable deep learning architecture design
   2. Scalable cloud computing service development
   3. backend development
- His previous research interests include mobile data mining and social media analysis.

EXPERIENCE
Software Engineer, HTC, Taipei, Taiwan — Nov. 2015-Present
Implemented and maintained cloud backend for mobile application with 1M+ users.
Enhance 60% test coverage for funfit application with 1M+ users.
EDUCATION
National Tsing Hua University, Hsinchu, Taiwan — July. 2013 - Aug. 2015, Master’s Degree, Information Systems and Applications (GPA : 3.84)

National Chung Hsing University, Taichung, Taiwan — Sep. 2009 - Jun. 2013, Bachelor’s Degree, Computer Science (GPA : 3.68)
SKILLS
Go, Linux, Docker, Java, Data Mining, Amazon Web Services(AWS), DevOps
LANGUAGES
Chinese — Native or bilingual proficiency
English — Limited working proficiency (Toeic Listen/Reading : 860, April, 2015)
PUBLICATION
Yao-Chung Fan, Yu-Chi Chen, Kuan-Chieh Tung, Kuo-Chen Wu, Arbee L.P. Chen,  A Framework for Enabling User Preference Profiling through Wi-Fi Logs, IEEE Transaction on Knowledge and Data Engineering (TKDE 2015) pdf

Kuan-Chieh Tung, En-Tzu Wang, Arbee L. P. Chen, Mining Event Sequences from Social Media for Election Prediction, Industrial Conference on Data Mining (ICDM 2016) link
PROJECT
Mining User Behavior For Better Service Provision to HTC customers (HTC industry and research plan, Jan. 2013- Dec. 2014, NTD:2,676,000)



2016年1月3日 星期日

[新訓]研發替代役49T 熾火25中心得分享 (生存技巧篇)


成功嶺的生活真的跟外面世界很不一樣,
第一次體驗, 受到蠻大的震撼教育,
成功嶺有自己的遊戲規則, 當然進去就好好遵守, 安穩過完16天比較重要
既來之責安之, 相信這段新訓日子會成為很多役男的回憶。

一.  公差
我是25中隊第3分隊 器材班的
簡單介紹有哪些幹部, 以及各個幹部負責的項目內容,

1. 打飯班

[新訓]研發替代役49T 熾火25中心得分享 (入營準備篇)


離新訓結訓有一段時間了,這陣子好不容易有空可以來分享在成功嶺的心得,
本篇文章是單純分享25中16天的小小心得回饋
可能會與許多文章類似, 還煩請見諒, 只希望能幫助到往後要進去受訓的役男們~

一. 入營前的準備
[必帶文件&證件]
1. 健保卡
2. 身分證
3. 畢業證書影本
4. 郵局帳號影本
5. 木刻印章 (沒用到)
6. 折抵役期正本文件

2015年5月18日 星期一

[多益]心得分享與準備方式


考試時間: 2015/04/26
測驗成績: 860 (L : 450, R : 410)

上次考試日期: 2010/12
測驗成績: 670 (L : 360, R : 310)


2014年1月19日 星期日

[Java]ArrayList 集合物件化

前文回顧:
Read file & ArrayList & String型態陣列轉換

這篇之前寫的網誌,有提到在處理一個檔案的時候應該要怎麼寫會比較好
經過一學期的磨練,發現到自己之前寫的這篇,寫法上較沒有彈性
也沒有真正的落實 Java 物件化的觀念
以及沒有展示出物件化強大的地方 和 ArrayList的好用之處

想想,假設今天我們要讀近來的一個檔案
內文有 <學生姓名  數學成績  英文成績>
顯然的,學生姓名我們會希望用String來儲存
數學成績跟英文成績會想要用 double或int來儲存,因為可能會對這些成績做加總運算
或是由高到低的排序運算
這時候如果用之前的方法,必須要再多寫一個for迴圈轉 型態
並且在處理上會沒有較多的彈性空間

所以以下附上我的Source code
並且說明ArrayList物件化的概念

2014年1月18日 星期六

[研究所]清大資應 X 資料探勘課程心得分享

課程名稱:資料探勘 (Data Mining) 
授課語言 : 英語
修課時間 : 2013/09-2014/01

好站連結:
資料探勘好讀版
這連結裡面有詳細的介紹data mining很多觀念
剛好他講的內容跟我用的書籍內容是一樣的
所以也可以很快的複習,敘述淺顯易懂
很推薦大家

評量方式 :
1.開學第一次課堂考
評量方式主要是聽力測驗
因為是英語授課的關係,所以老師希望藉由這測驗
來先有個背景確認哪些學生是可以聽得懂老師的上課內容的
如果沒有通過的話,之後上課必須簽到,一次沒簽到扣三分

技術提供:Blogger.