天天看點

深度學習 與 C++ 的愛恨情仇

深度學習 與 C++ 的愛恨情仇

本文涉及的相關教程和代碼:

  • CPP-Call-Tensorflow
  • C++ 調用Python(TensorFlow)程式
  • https://github.com/BIGBALLON/CPP-Call-Tensorflow

需要c++資料加入小編c/c++程式設計群:825414254

深度學習 與 C++ 的愛恨情仇
  • Caffe2-Tutorial
  • 使用Caffe2 Python API建立并訓練LeNet,并正确存weight
  • 使用Caffe2 Python API建立并訓練ResNet, 不使用lmdb, 并正确存取帶有BN的weight
  • 使用Caffe2 C++ API 讀取預訓練model并進行預測
  • 使用Caffe2 Python API 進行 Multi-GPU訓練
  • 将 Caffe 的 weight 轉化為 Caffe2 的 weight, 并使用Caffe2 C++進行讀取與預測
  • 将 PyTorch 的 weight 轉化為 Caffe2 的 weight, 并使用Caffe2 C++進行讀取與預測
  • https://github.com/BIGBALLON/Caffe2-Tutorial
  • PyTorch-CPP
  • 使用PyTorch C++ API (Libtorch) 讀取預訓練model并進行預測
  • https://github.com/BIGBALLON/PyTorch-CPP
OK,下面開始講故事, 不想聽故事的可以直接把代碼抱走,不用客氣,請随意啦。

0. 初識 Deep Learning

大約1年半前至2年前的樣子,選修了學校的 Deep Learning and Practice,第一次接觸深度學習,記得那時候大家用的多半是TensorFlow,第一個作業是實作 NIN(Network In Network),弄了半天因為initial的問題死活train不起來,神奇的舍友推薦我加一下BN試試,搞了半天TensorFlow有N個batch_normal的函數就是不知道怎麼用,于是神奇的舍友又推薦我用一個叫做Keras的東西,我靠,BN隻要一行。更神奇的是,加完BN後train得飛起,于是乎就寄信去問助教,可不可以用BN,第二天助教課得到得答複是,BN是 homework2 要求使用的trick,當時沒别的,就覺得舍友好猛,啥都懂。

噢,跑偏了,回來回來。後來我們有一個需求,需要在C++中調用keras(tf),查了一下有好多種方法,選了原生支援的方法,就加個頭檔案Python.h,然後瞎一頓搞就好了,

代碼見 CPP-Call-Tensorflow:https://github.com/BIGBALLON/CPP-Call-Tensorflow

深度學習 與 C++ 的愛恨情仇

1. Caffe2 與 C++

不經意間,身邊的朋友幾乎清一色都轉向了PyTorch,而因為對 C++ 有特殊的需求,Lab的學長們還在使用Caffe。唔,然後Caffe2就出來了,原生支援 Python 和 C++。 于是我們就轉向了 Caffe2

三種做法:

  • 最直接的是用Caffe2 Python API 建network并完成訓練,導出model,再用 C++ 進行加載與使用
  • 當然,後來也會用Caffe2 Python API 拉出network,直接導出,在C++ 端完成所有的後續工作,包括訓練,以及訓練好後的導出model,inference等等。
  • 最暴力的是直接放棄Caffe2 Python API, 完全使用 C++ 完成 建network,training,inference等所有的工作。

中間也遇到些許小坑,說出來你可能不信,Caffe2剛出來的時候,整個issue區問最多的問題就是weight怎麼存和weight怎麼讀。後來都在問帶有BN的network weight怎麼存,當然更多的人是在問Caffe2為什麼那麼難裝,哈哈。

深度學習 與 C++ 的愛恨情仇

當然Caffe2 還有一個好處, 能夠将 Caffe的 weight 輕易地轉化為 Caffe2 的 weight

不知不覺,Caffe2 猛然間和PyTorch合并了,PyTorch0.4 出現了, 與此同時一個叫做ONNX(Open Neural Network Exchange)的東西,它也lei le。 于是我們可以将weight 在不同的DL framework間互相轉換。其中ONNX對Caffe2的支援也非常好。 于是我們又有了新的做法:

  • 在PyTorch下進行訓練,然後通過ONNX導出model,再将onnx model 轉化為Caffe2 model。

值得吐槽的地方是,自從Caffe2 與 PyTorch合并後,官方的文檔也長期不更新了,可以觀察到Github上面關于Caffe2的issue也常常沒有人回複,Caffe2的C++端進行了很大幅度的修改,官方也沒有任何的文檔說明,你想用的話就自己遇坑自己解決就對了。

在此期間也學會了一個小技巧:

通常情況下,你的程式就算是編譯好,也無法直接從copy到其他機器上運作,因為你的程式需要用到cuda,cudnn,caffe2等等。怎麼辦,把它們打包起來,一起帶走:

以Ubuntu16.04為例,首先先安裝這兩個工具:

sudo apt install chrpath patchelf
      

然後進行打包操作,假設你的程式名字叫做 test, shell檔案 pack.sh 如下

ldd test | grep "=> /" | awk '{print $3}' | xargs -I '{}' cp -v '{}' .
rm libcuda.so*
rm libnvidia-fatbinaryloader.so*
rm libc.so.6
ls -1 lib* | xargs -t -n1 patchelf --set-rpath .
cp /lib/x86_64-linux-gnu/libc.so.6 .
strip test
chrpath -r . test
      
打包前
┌─[[email protected]] - [~/Documents/czf/debug] - [2019-01-19 01:49:54]
└─[0] ls
pack.sh test
打包後
┌─[[email protected]] - [~/Documents/czf/debug] - [2019-01-19 01:50:21]
└─[0] ls
libatk-1.0.so.0 libfontconfig.so.1 libgraphite2.so.3 libk5crypto.so.3 libopencv_highgui.so.2.4 libschroedinger-1.0.so.0 libusb-1.0.so.0 libXcursor.so.1
libavcodec-ffmpeg.so.56 libfreetype.so.6 libgsm.so.1 libkeyutils.so.1 libopencv_imgproc.so.2.4 libselinux.so.1 libutil.so.1 libXdamage.so.1
libavformat-ffmpeg.so.56 libgcc_s.so.1 libgssapi_krb5.so.2 libkrb5.so.3 libopenjpeg.so.5 libshine.so.3 libuuid.so.1 libXdmcp.so.6
libavutil-ffmpeg.so.54 libgcrypt.so.20 libgtkglext-x11-1.0.so.0 libkrb5support.so.0 libopen-pal.so.13 libSM.so.6 libv4l1.so.0 libXext.so.6
libbluray.so.1 libgdkglext-x11-1.0.so.0 libgtk-x11-2.0.so.0 libleveldb.so.1 libopen-rte.so.12 libsnappy.so.1 libv4l2.so.0 libXfixes.so.3
libbz2.so.1.0 libgdk_pixbuf-2.0.so.0 libHalf.so.12 liblmdb.so.0 libopus.so.0 libsoxr.so.0 libv4lconvert.so.0 libXinerama.so.1
libcaffe2_gpu.so libgdk-x11-2.0.so.0 libharfbuzz.so.0 libltdl.so.7 liborc-0.4.so.0 libspeex.so.1 libva.so.1 libXi.so.6
libcaffe2.so libgflags.so.2 libhogweed.so.4 liblzma.so.5 libp11-kit.so.0 libssh-gcrypt.so.4 libvorbisenc.so.2 libxml2.so.2
libcairo.so.2 libgio-2.0.so.0 libhwloc.so.5 libmodplug.so.1 libpango-1.0.so.0 libstdc++.so.6 libvorbis.so.0 libXmu.so.6
libcom_err.so.2 libglib-2.0.so.0 libibverbs.so.1 libmp3lame.so.0 libpangocairo-1.0.so.0 libswresample-ffmpeg.so.1 libvpx.so.3 libXrandr.so.2
libcrystalhd.so.3 libglog.so.0 libICE.so.6 libmpi_cxx.so.1 libpangoft2-1.0.so.0 libswscale-ffmpeg.so.3 libwavpack.so.1 libXrender.so.1
libc.so.6 libGL.so.1 libicudata.so.55 libmpi.so.12 libpangox-1.0.so.0 libtasn1.so.6 libwebp.so.5 libXt.so.6
libcublas.so.8.0 libGLU.so.1 libicuuc.so.55 libm.so.6 libpcre.so.3 libtbb.so.2 libX11.so.6 libxvidcore.so.4
libcudnn.so.7 libgme.so.0 libidn.so.11 libnettle.so.6 libpixman-1.so.0 libthai.so.0 libx264.so.148 libz.so.1
libcurand.so.8.0 libgmodule-2.0.so.0 libIex-2_2.so.12 libnuma.so.1 libpng12.so.0 libtheoradec.so.1 libx265.so.79 libzvbi.so.0
libdatrie.so.1 libgmp.so.10 libIlmImf-2_2.so.22 libnvidia-glcore.so.410.78 libpthread.so.0 libtheoraenc.so.1 libXau.so.6 pack.sh
libdc1394.so.22 libgnutls.so.30 libIlmThread-2_2.so.12 libnvidia-tls.so.410.78 libraw1394.so.11 libtiff.so.5 libxcb-render.so.0 test
libdl.so.2 libgobject-2.0.so.0 libjasper.so.1 libnvrtc.so.8.0 libresolv.so.2 libtwolame.so.0 libxcb-shm.so.0
libexpat.so.1 libgomp.so.1 libjbig.so.0 libogg.so.0 librtmp.so.1 libudev.so.1 libxcb.so.1
libffi.so.6 libgpg-error.so.0 libjpeg.so.8 libopencv_core.so.2.4 librt.so.1 libunwind.so.8 libXcomposite.so.1
      

可以看到需要的庫檔案都打包進來了,而且程式執行的時候不會再去系統的路徑中找,而是直接在目前目錄找,把整個檔案夾copy到其他機器去吧,現在你可以随意地執行它了。

上述Caffe2的使用(C++ inference,caffe轉caffe2,pytorch轉caffe2),

代碼見 Caffe2-Tutorial

2. PyTorch 與 C++

随着PyTorch不斷的完善,身邊棄坑TF,投奔PyTorch的朋友就沒有再回來的了。随着PyTorch1.0的釋出,C++ 似乎有了新的曙光,現在我們可以這麼做:

  • 在PyTorch python 端訓練model,并通過jit導出,然後使用PyTorch C++ API 編寫需要的應用,編譯即可上線。代碼見 PyTorch-CPP
  • 完全在PyTorch C++ API中拉net,training,inference等,say goodbye for python 。
深度學習 與 C++ 的愛恨情仇

3. Deep Learning 與 C++ 的 未來

Deep Learning 發展到現在,C++ 方面還是不能夠随心所欲地coding, 希望在未來半年到一年内還會有新的突破。 期待一下能像寫python一樣随心所欲地寫 C++ 程式!!