어제 C++에서 Boost Python을 이용한 Tensorflow 설치 및 예제 실행에 대한 포스팅을 마치고 나서 좀 더 익숙해지기 MNIST의 예제 코드를 C++에 옮겨봤습니다.
exec, eval, attr의 적재적소 사용에 대한 어려움이 있었지만 지금은 해내서 기쁩니다.

Python에선 당연하게 썼던 여러 개의 리턴값이나 Dictionary 문법, from ... import 를 구현하는데 시간을 많이 썼고 엄격한 계산법(Strict evaluation)으로 당연히 될 것 같은 문장도 차근 차근 나눠써야되는 부분이 있는 등 신경써야 할 부분이 많았습니다.
직접 찾아본 것도 있지만 역시 홍정모 교수님 블로그에서 참고한 소스들이 도움이 많이 되었습니다.



<구성 환경>


Visual Studio 2015 Community Update 3

boost_1_65

Anaconda Virtual Environment Python 3.5.x


<참고자료>



3. 텐서플로우(TensorFlow)를 이용한 MNIST 문자 인식 프로그램 만들기

http://solarisailab.com/archives/303


Tensorflow FCNN C++로만 구현하기

http://blog.naver.com/atelierjpro/221056571787


Boost Python으로 Tensorflow를 C++에서 더 쉽게 쓰기

http://blog.naver.com/atelierjpro/221054333725



C++로 옮길 MNIST의 파이썬 코드 예제입니다.


# -*- coding: utf-8 -*-
 
# MNIST 데이터를 다운로드 한다.
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
 
# TensorFlow 라이브러리를 추가한다.
import tensorflow as tf
 
# 변수들을 설정한다.
= tf.placeholder(tf.float32, [None, 784])
= tf.Variable(tf.zeros([78410]))
= tf.Variable(tf.zeros([10]))
= tf.nn.softmax(tf.matmul(x, W) + b)
 
# cross-entropy 모델을 설정한다.
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
print(train_step)
 
# 경사하강법으로 모델을 학습한다.
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  #sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
  sess.run(train_step, feed_dict={x : batch_xs, y_ : batch_ys})
 
# 학습된 모델이 얼마나 정확한지를 출력한다.
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
cs




제가 C++로 옮겨본 소스입니다.


초반에는 exec과 eval를 일부러 많이 쓰다가 점점 익숙해지면서 최대한 exec과 eval을 줄여보려고 했습니다.


하지만 mnist에 대한 데이터를 py::object 객체로 받아온 것은 좋았는데 그 이후에 mnist.train.next_batch(100) 줄을 실행할 때 배열의 인덱스는 특정한 정수여야한다는 에러가 떠서 그냥 파이썬 모듈에서도 import 시켜주고 eval로 받아왔습니다.


#define BOOST_PYTHON_STATIC_LIB
#define BOOST_LIB_NAME "boost_numpy3"
#include <boost/config/auto_link.hpp>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include <iostream>
 
namespace py = boost::python;
namespace np = boost::python::numpy;
 
int main()
{    
    using namespace std;
 
    try
    {
        Py_SetPythonHome(L"D:\\Equip\\Anaconda3\\envs\\py35_tf_cpp");
 
        Py_Initialize();
        np::initialize();
 
        PyRun_SimpleString("#-*- coding: utf-8 -*-");        
        py::object main_module = py::import("__main__");
        py::object main_namespace = main_module.attr("__dict__");
        py::object sys_ = py::import("sys");
        
        PyRun_SimpleString("import sys\n"
            "sys.argv = ['']");
 
        py::object print = py::import("__main__").attr("__builtins__").attr("print");
 
        const py::object tf_ = py::import("tensorflow");
        py::exec("import tensorflow as tf", main_namespace);
        
        const py::object input_data_ = py::import("tensorflow.examples.tutorials.mnist").attr("input_data");        
        py::object mnist = input_data_.attr("read_data_sets")("MNIST_data"true);
                
        py::exec("from tensorflow.examples.tutorials.mnist import input_data", main_namespace);
        py::exec("mnist = input_data.read_data_sets('MNIST_data /', one_hot=True)", main_namespace);
                
        //Set variables
        py::object x = py::eval("tf.placeholder(tf.float32, [None, 784])", main_namespace);
        py::object W = py::eval("tf.Variable(tf.zeros([784, 10]))", main_namespace);
        py::object b = py::eval("tf.Variable(tf.zeros([10]))", main_namespace);
        py::object y = tf_.attr("nn").attr("softmax")(tf_.attr("matmul")(x, W) + b);        
 
        //Set Cross-Entropy Model
        py::object y_ = py::eval("tf.placeholder(tf.float32, [None, 10])", main_namespace);
        
        py::object sum = tf_.attr("reduce_sum")(y_ * tf_.attr("log")(y), 1);
        py::object cross_entropy = tf_.attr("reduce_mean")(-1 * sum);
        py::object train_step = tf_.attr("train").attr("GradientDescentOptimizer")(0.5).attr("minimize")(cross_entropy);
 
        // Learn model using SGD
        py::object init = tf_.attr("initialize_all_variables")();
        py::object sess = tf_.attr("Session")();
        sess.attr("run")(init);
 
        for (int i = 0; i < 1000; i++)
        {    
            py::object batches = py::eval("mnist.train.next_batch(100)", main_namespace);
            py::object batch_xs = batches[0];
            py::object batch_ys = batches[1];            
 
            py::dict feed_dict;
            feed_dict[x] = batch_xs;
            feed_dict[y_] = batch_ys;
 
            sess.attr("run")(train_step, feed_dict);        
        }
            
        // Print Accuracy
        py::object f32 = tf_.attr("float32");
        py::object correct_prediction = tf_.attr("equal")(tf_.attr("argmax")(y, 1), tf_.attr("argmax")(y_, 1));
        py::object cast = tf_.attr("cast")(correct_prediction, f32);
        py::object accuracy = tf_.attr("reduce_mean")(cast);
 
        py::dict feed_dict;
        py::object test_x = py::eval("mnist.test.images", main_namespace); 
        py::object test_y = py::eval("mnist.test.labels", main_namespace);
 
        feed_dict[x] = test_x;
        feed_dict[y_] = test_y;
 
        print(sess.attr("run")(accuracy, feed_dict));
    }
    catch (py::error_already_set&)
    {
        PyErr_Print();
        system("pause");
    }
 
    return 0;
}
cs



실행하면 아래와 같은 결과를 얻을 수 있습니다.


Extracting MNIST_data /train-images-idx3-ubyte.gz

Extracting MNIST_data /train-labels-idx1-ubyte.gz

Extracting MNIST_data /t10k-images-idx3-ubyte.gz

Extracting MNIST_data /t10k-labels-idx1-ubyte.gz


(Warning 및 GPU 정보 생략)


0.9189


'연구 > 딥러닝' 카테고리의 다른 글

Visual Studio에서 C++로 TensorFlow 실행하기  (3) 2017.08.28
최근 일주일동안 C++에서 Tensorflow를 사용하기 위해 온갖 삽질을 다 했습니다.
어제가 딱 일주일이 되던 날이었는데 C++에서 직접 Tensorflow를 사용하는 것은 다양한 에러와 싸우며 실패했고
Boost Python을 이용하여 C++와 Tensorflow 사이에 Python을 끼워주는 방법을 찾아서 5시간 만에 성공했습니다.

Vcpkg 에서 Tensorflow를 지원할 때까지 이렇게 쓰려고 합니다.

근 일주일동안, 그리고 앞으로도 신세를 많이 지게 될 것 같은 블로그가 홍 정모 교수님의 블로그(클릭하면 이동)일 것 같습니다.
블로그에서 댓글로도 감사 인사를 남겼는데 제 일기나 다름없는 여기에도 감사 인사를 아끼지 않고 싶습니다.


<구성 환경>


Visual Studio 2015 Community Update 3

boost_1_65

Anaconda Virtual Environment Python 3.5.x


<참고자료>



C++ Boost Library 1.63 빌드 (Python/Numpy 빌드)

http://blog.naver.com/atelierjpro/220965925819


Boost Python으로 Tensorflow를 C++에서 더 쉽게 쓰기

http://blog.naver.com/atelierjpro/221054333725


AttributeError due to sys.argv[0] for 'import tensorflow as tf' inside c

https://stackoverflow.com/questions/41323576/attributeerror-due-to-sys-argv0-for-import-tensorflow-as-tf-inside-c


Boost.PythonおよびBoost.NumpyをWindowsで使うまで

http://tatsyblog.sakura.ne.jp/wordpress/programming/python/1308/



1. VS와 Anaconda를 설치합니다.


2. 미리 Anaconda에서 파이썬 가상 환경을 기호에 맞게 설정합니다. 저는 python 3.5에 tensorflow만 올렸습니다.


(in Anaconda Prompt)


conda create -n py35_tf_cpp python=3.5

activate py35_tf_cpp

pip install --ignore-installed --upgrade tensorflow-gpu


gpu가 마땅치 않은 경우 cpu 버전을 설치해주세요. (--upgrade tensorflow)



3. boost를 다운받고 적당한 곳에 압축을 해제합니다. 저는 boost_1_65_0.zip을 받았습니다.


4. VS 폴더 아래에 있는 vcvars64.bat을 실행하고 3에서 압축을 해제한 boost 폴더 안의 bootstrap을 실행합니다.


(in Anaconda Prompt)


"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"

bootstrap.bat


5. bootstrap.bat 을 성공적으로 실행하고나면 같은 폴더에 project-config.jam 파일이 생성됩니다. 이 파일을 수정하고 저장한 후 boost를 빌드합니다.


[ project-config.jam ]


import option ; 

 

using msvc ; 

using python : 3.5 : D:\Equip\Anaconda3\envs\py35_tf_cpp : : : <address-model>64 ; 

 

option.set keep-going : false ; 


반드시 64와 ; (세미콜론) 사이의 간격이 띄워져 있어야한다고 합니다.


(in Anaconda Prompt)

b2 -j8 --toolset=msvc-14.0 --build-type=complete architecture=x86 address-model=64 stage


-j 뒤의 숫자는 쓰레드 갯수이니 CPU 사양에 따라 조절해주세요.



6. VS에서 Win32 Console Application의 빈 프로젝트를 하나 생성하고 추가 포함 디렉터리(Additional Include Directories)와 추가 라이브러리 디렉터리(Additional Library Directories)에 boost의 폴더와 python 가상환경의 폴더를 추가합니다. 저는 Release x64 환경에서 진행하였습니다.


예시)


Additional Include Directories (C/C++ / General)

D:\Anaconda3\envs\py35_tf_cpp\include

D:\boost\boost_1_65_0_cpp_tf


Additional Library Directories (Linker / General)

D:\boost\boost_1_65_0_cpp_tf\stage\lib

D:\Anaconda3\envs\py35_tf_cpp\libs


Additional Dependencies (Linker / Input)

python35.lib

libboost_numpy3-vc140-mt-1_65.lib




7. cpp 파일을 하나 만들고 아래의 예시 코드를 참조하여 코드를 완성한 후 빌드합니다. 


#define BOOST_PYTHON_STATIC_LIB
#define BOOST_LIB_NAME "boost_numpy3"
#include <boost/config/auto_link.hpp>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include <iostream>
 
namespace py = boost::python;
namespace np = boost::python::numpy;
 
int main()
{
    using namespace std;
 
    Py_SetPythonHome(L"D:\\Equip\\Anaconda3\\envs\\py35_tf_cpp");
 
    Py_Initialize();
    np::initialize();
 
    py::object main_module = py::import("__main__");
    py::object main_namespace = main_module.attr("__dict__");
 
    py::object sys_ = py::import("sys");
    PyRun_SimpleString("import sys\n"
        "sys.argv = ['']");
    string version = py::extract<string>(sys_.attr("version"));
    cout << version << endl;
 
    py::object print = py::import("__main__").attr("__builtins__").attr("print");
    print("Hello, Python");
 
    const py::object tf_ = py::import("tensorflow");
 
    const np::ndarray d1 = np::array(py::make_tuple(1.0f, 2.0f, 3.0f, 4.0f));
    const np::ndarray d2 = np::array(py::make_tuple(5.0f, 6.0f, 7.0f, 9.0f));
 
    const py::object x1 = tf_.attr("constant")(d1);
    const py::object x2 = tf_.attr("constant")(d2);
 
    const py::object result = tf_.attr("multiply")(x1, x2);
    const py::object sess = tf_.attr("Session")();
 
    print(sess.attr("run")(result));
 
    sess.attr("close");    
 
    return 0;
}
cs



8. boost 를 빌드했던 폴더 안에 있는 stage\lib 폴더에서 boost_python3-vc140-mt-1_65.dll 과 boost_numpy3-vc140-mt-1_65.dll 를 7에서 빌드한 폴더에 복사합니다.


9. 실행하면 아래와 같은 결과를 얻을 수 있습니다.


3.5.4 |Continuum Analytics, Inc.| (default, Aug 14 2017, 13:41:13) [MSC v.1900 64 bit (AMD64)]

Hello, Python


(GPU 정보 생략)


[  5.  12.  21.  36.]

계속하려면 아무 키나 누르십시오 . . .


'연구 > 딥러닝' 카테고리의 다른 글

C++ 에서 Boost Python으로 MNIST 실습 예제  (1) 2017.08.29

+ Recent posts