shamangary's Blog

About Me

| Comments

楊存毅 (Tsun-Yi Yang)

這是我的個人網頁,我來自臺灣,先前就讀於台灣大學物理系、台灣大學應用物理研究所,現在就讀於台灣大學資訊學系博士班,並於中研院CVLAB擔任研究助理
This is my personal website. I come from Taiwan.
NTU Physics bachelor
NTU Applied Physics master
NTU Computer Science and Information Engineering PhD student (PhD candidate now!)
Academia Sinica CVLAB research assistant
ULSEE algorithm engineer (2017/07~2017/12)

E-mail: shamangary@hotmail.com
Github: https://github.com/shamangary
LinkedIn: https://www.linkedin.com/in/tsun-yi-yang-65248795/


Congradutaions to Tsun-Yi Yang, Yi-Husan Huang, Yen-Yu Lin, Pi-Cheng Hsiu, and Yung-Yu Chuang! Our work entitled "SSR-Net: A Compact Soft Stagewise Regression Network for Age Estimation" has been accepted for IJCAI 2018.
Project Page: https://github.com/shamangary/SSR-Net


Congradutaions to Tsun-Yi Yang, Jo-Han Hsu, Yen-Yu Lin, and Yung-Yu Chuang! Our work entitled "DeepCD: Learning Deep Complementary Descriptors for Patch Representations" has been accepted for ICCV 2017.
Project Page: https://github.com/shamangary/DeepCD


Congradutaions to Tsun-Yi Yang, Yen-Yu Lin, and Yung-Yu Chuang! Our work entitled "Accumulated Stability Voting: A Robust Descriptor from Descriptors of Multiple Scales" has been accepted for CVPR 2016.
Project Page: http://shamangary.logdown.com/posts/587520

Code explanation in center loss github

| Comments

由於我之前看了蘇大大的這篇文章 https://kexue.fm/archives/4493/
覺得寫得很棒,又剛好想要做center loss,所以就做了一個github,
https://github.com/shamangary/Keras-MNIST-center-loss-with-visualization
除了蘇大大的center loss概念外,我加入了MNIST的訓練過程,
還有視覺化來確認是否有真正聚合特徵點到所謂的中心特徵,
算是成為了我keras的入門,可是好像有人寄信問我說覺得我寫的看不懂,
所以特此解釋一下。

1. 可以解釋一下概念嗎?

所謂的損失函數,在keras中的最終目的是要training,
所以在model.compile過後可以被視為損失函數,

由於center loss這東西除了softmax之外又加了所謂的center和特徵之間的距離項,
所以我定義的model_centerloss.compile的那行會有兩種loss,

第一個是categorical_crossentropy,也就是一般classfication的loss,
第二的應該是center loss了吧,可是為何要在loss的地方寫

lambda y_true,y_pred: y_pred

我猜你是在疑惑這個

我一開始學keras的時候也看不懂蘇大大這裡寫的概念,其實這是說keras的損失函數定義,
必然要有y_true和y_pred,就是keras他定死的,
那麼這裡寫lambda y_true,y_pred: y_pred的意思就是一個匿名函數,
就是不管你輸入了什麼ground-truth(y_true),我都回傳你y_pred,
那又是為什麼這樣寫?因為前面的輸入是l2_loss的層,這層的輸出就直接是要最小化的目標了,
所以有別於一般的categorical_crossentropy,
在l2_loss的fit,由於keras定義你必須要輸入對應標籤對(y_true),
但是實際上你又不需要(因為l2_loss直接是想要的損失函數項),
所以我們才用了lambda y_true,y_pred: y_pred,
並且在fit的過程中會定義隨機但是剛好維度符合的y值作為一個假的y_true,

random_y_train = np.random.rand(x_train.shape[0],1)
random_y_test = np.random.rand(x_test.shape[0],1)

這部分牽扯到keras對損失函數的定義問題,所以剛開始看可能會看不懂。
而兩個損失函數的結合有權重項loss_weights=[1,lambda_c],
最後在訓練的過程中,如果你不要center loss,當然是直接用model.fit就好了,
相反如果你需要center loss,那也是用model_centerloss.fit就好了,
因為model_centerloss同時包含了有softmax和center loss,並且已經用權重項組合起來了。

2. L2 loss怎麼看?感覺好怪?

在center loss裡面有個一行,定義中心和特徵之間的距離:

l2_loss = Lambda(lambda x: K.sum(K.square(x[0]-x[1][:,0]),1,keepdims=True),name='l2_loss')([ip1,centers])

乍看之下會有點難懂,我把它展開來看應該會清楚一點

def l2_loss_fun(x):
    print(x)
    ip1 = x[0]
    centers = x[1][:,0]
    print(ip1.shape)
    print(centers.shape)
    sq_dis = K.square(ip1-centers)
    print(sq_dis.shape)
    l2_loss_temp = K.sum(sq_dis,1,keepdims=True)
    print(l2_loss_temp.shape)
    return l2_loss_temp

l2_loss = Lambda(l2_loss_fun)([ip1, centers])

上面這個展開和原本的一行式子是完全等價的,
只是我們可以在函數裡比較好看到各種特徵的維度,
執行程式後回傳

[<tf.Tensor 'ip1/add:0' shape=(?, 2) dtype=float32>, <tf.Tensor 'embedding_1/Gather:0' shape=(?, 1, 2) dtype=float32>]
(?, 2)
(?, 2)
(?, 2)
(?, 1)

首先第一個回傳值:

[<tf.Tensor 'ip1/add:0' shape=(?, 2) dtype=float32>, <tf.Tensor 'embedding_1/Gather:0' shape=(?, 1, 2) dtype=float32>]

輸入的x有兩個東西,ip1和center,其維度分別為(?,2)和(?,1,2),
為了使其相減,我們用x[0]和x[1][:,0]重新命名他們,
可以看到印出來的維度是

(?, 2)
(?, 2)

接著我們算距離,先相減再平方,回傳維度為

(?, 2)

接著我們加起來,回傳維度為

(?, 1)

這個地方我就沒有開根號了,如果要開也可以就再加一行就好。

[Source code study] Rewrite StarGAN. From Pytorch to Keras. Part. 1

| Comments

由於之前有想要研究GAN來融合個神奇寶貝,但是之前試過DCGAN和WGAN其實覺得效果都很爛,然後StarGAN就橫空殺出,他們的DEMO讓我非常驚訝,又是multi-domain又是漂亮的影像輸出還有code,馬上列為必讀必寫的課題,接著就來改寫他們的pytorch到keras的形狀吧:D。

讀code的時候我喜歡從易看的地方下手,例如說雖然pytorch和keras都是python based的,但是你其實很難知道main.py需不需要改寫,所以其實用一般CNN的角度來看,model.py肯定是需要改寫的,那我們就從model.py開始出發。

[Eng. ver.] This is a note for rewriting StarGAN from pytorch to keras. I record my steps and thoughts in this note. If you find anything wrong or something you want to add, please contact me or comment below.

In this note, we first review the model.py first. Since every framework (pytorch, keras, ...) must require their own special coding method, it is pretty clear that this file contains pure pytorch, and we want to edit it first.

Goal

# Pytorch -> Keras

# Sequential model -> functional API

1. Conv2D/Conv2DTranspose

# Pytorch

# ref
  torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

# source code
  layers.append(nn.Conv2d(3+c_dim, conv_dim, kernel_size=7, stride=1, padding=3, bias=False))
        

----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------

# Keras

# ref
  keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)

  keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)

# reimplementation

  x = ZeroPadding2D(padding=(3,3))(x)
  x = Conv2D(conv_dim ,(7,7), strides=(1,1), use_bias=False)(x)
  
  or
  
  x = Conv2D(conv_dim ,(7,7), strides=(1,1), padding='same', use_bias=False)(x)
  • There is no need to use input dimension of the layer in keras.

Conv2DTranspose

*** Somehow zero padding with Conv2DTranspose in Keras is not equal to nn.ConvTranspose2d in Pytorch. Use padding='same' directly!!! ***

# Pytorch
# source code
  layers.append(nn.ConvTranspose2d(curr_dim, curr_dim//2, kernel_size=4, stride=2, padding=1, bias=False))
----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------
# Keras
# reimplementation
    x = Conv2DTranspose(curr_dim//2 ,(4,4), strides=(2,2), padding='same', use_bias=False)(x)
            

2. Instance normalization

(Not directly supported by Keras. You need to install keras-contrib: https://github.com/keras-team/keras-contrib)

# Pytorch

# ref
  torch.nn.InstanceNorm2d(num_features, eps=1e-05, momentum=0.1, affine=False)
  affine: a boolean value that when set to ``True``, gives the layer learnable affine parameters. Default: ``False``

# source code
  layers.append(nn.InstanceNorm2d(conv_dim, affine=True))
        
----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------
# Keras

# ref
  https://github.com/keras-team/keras-contrib/blob/master/keras_contrib/layers/normalization.py

# reimplementation

  x = InstanceNormalization()(x)

3. ReLU/LeakyReLU

# Pytorch

# ref
  torch.nn.ReLU(inplace=False)

  torch.nn.LeakyReLU(negative_slope=0.01, inplace=False)

# source code
  layers.append(nn.ReLU(inplace=True))

  layers.append(nn.LeakyReLU(0.01, inplace=True))

----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------

# Keras

# ref
  relu(x, alpha=0.0, max_value=None)

  keras.layers.LeakyReLU(alpha=0.3)

# reimplementation
  x = Activation('relu')(x)

  x = LeakyReLU(alpha=0.01)(x)

4. unsqueeze and expand (in Pytorch)

# Pytorch

def forward(self, x, c):
        # replicate spatially and concatenate domain information
        c = c.unsqueeze(2).unsqueeze(3)
        c = c.expand(c.size(0), c.size(1), x.size(2), x.size(3))
        x = torch.cat([x, c], dim=1)
        return self.main(x)

----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------
# Keras

inputs = Input(shape=self._input_shape) # remember input image channel is (RGB+mask channel)

fake_c = Input(shape=(self.c_dim,))

def labels_to_maps(x, image_size):
    x_temp = K.expand_dims(x,1)
    x_temp = K.expand_dims(x_temp,2)
    x_temp = K.tile(x_temp, (1, image_size, image_size, 1))
    return x_temp

fake_c_2d = Lambda(labels_to_maps, arguments={'image_size': self.image_size})(fake_c)
x = Concatenate(axis=-1)([inputs, fake_c_2d])

Note that unsqueeze() in pytorch is corresponding to expand_dims() in keras.
And expand() in pytorch is corresponding to tile() in keras.

5. squeeze (in Pytorch)

# pytorch

Returns a tensor with all the dimensions of input of size 1 removed.
http://pytorch.org/docs/master/torch.html

# source code
    out_real.squeeze(), out_aux.squeeze()
    
----------------------------------------------
|
| (rewrite the layer into keras)
|
v
----------------------------------------------

# keras

def squeeze_all(x):
    x_temp = x
    delta_temp = 0
    for i in range(1,4):
    if x.shape[i]==1:
        x_temp = K.squeeze(x_temp,axis=(i-delta_temp))
        delta_temp = delta_temp + 1
    return x_temp
    
out_real = Lambda(squeeze_all)(out_real)
out_aux = Lambda(squeeze_all)(out_aux)

Note that the squeeze in tensorflow or keras is not equal to the squeeze in pytorch.
You must define your own custom layer to do that.

Check (Full code will be released soon)

KERAS_BACKEND=tensorflow python
# python code
from model_keras import Generator, Discriminator
import numpy as np

img_size = 128
g_model = Generator(img_size)()
d_model = Discriminator(img_size)()

m = np.random.rand(1,img_size,img_size,3)
fake_c = np.random.rand(1, 5)

pred_g = g_model.predict([m, fake_c])
pred_d = d_model.predict(m)


pred_g.shape
pred_d[0].shape
pred_d[1].shape

# output
>>> pred_g.shape
(1, 128, 128, 3)
>>> pred_d[0].shape
(1, 2, 2)
>>> pred_d[1].shape
(1, 5)

Caffe installation with anaconda in one line (with solvable bugs)

| Comments

因為anaconda有很多版本的caffe,
各種版本:https://anaconda.org/search?q=caffe
然而通常使用的conda-forge安裝後一堆問題,
python裏無法import caffe,所以我就試了各種版本,
目前是在python2.7會work
(python3.5即使用intel版本也不會work,安裝protobuf=3.2也沒有用)

創建新環境並進入

$conda create -n py2cv3 python=2.7
$source activate py2cv3
(py2cv3)$

安裝intel版本的caffe

(py2cv3)$conda install -c intel caffe

安裝會很長時間因為他們有很多東西,之後可能會有numpy的error

RuntimeError: module compiled against API version 0xb but this version of numpy is 0xa

這好像是因為有不同的numpy相衝,所以我們刪除他後重裝

(py2cv3)$pip uninstall numpy
(py2cv3)$conda install numpy

注意不能用conda uninstall numpy因為似乎會連帶刪除一些其他的連結庫

最後要check

(py2cv3)$python
>>>import caffe

沒錯誤應該就OK

這樣應該就可以了,不過我希望其他版本的以後會把一些奇怪的bug給修好就是,像是什麼libprotobuf之類的bug,網路上的修正我都無法用好,最後採用了intel的版本只有numpy的問題就順利解決了。

安裝Opencv 3.1.0,環境:python2, python3(opencv3,dlib,keras,tensorflow,pytorch)

| Comments

現在這個時代你的電腦都會安裝一堆不同版本的python和opencv,我們就安裝一次近年的東西當做例子看看其中的問題
先講結論:請愛用anaconda,真的,我沒有騙你,我查了好久嗚嗚。

1. Install opencv3.1.0 on python2.7 (不推薦,但是可用: not recommended, but useful)

=============先看下面一個項再執行這些指令,如果有cuda8.0是會出錯的=============

cd ~/opencv-3.1.0
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make -j8
sudo make install

(-j8是指用幾個核心去處理會快非常多,數量以你的電腦規格為主,我的電腦好像可以到12)

How to fix the error: [cuda8.0 is not match with opencv3.1.0]

http://blog.csdn.net/yiranyhy/article/details/72935499

解决方法:

打开/home/yhy/opencv-3.0.0/modules/cudalegacy/src/graphcuts.cpp

把此处改为:

#include "precomp.hpp"

#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER)  ||(CUDART_VERSION>=8000)

其中 ||(CUDART_VERSION>=8000)为后增加的内容

How to check?

$python
>>>import cv2
>>>cv2.__version__

沒有出現錯誤就是安裝正確

2. Install opencv3.1.0 on python3 (非常推薦: recommended)

如果你同時有python2和python3,上面的安裝可能只會讓你安裝到python2,你會發現你在python3中要import cv2會失敗!!!要解決的話要特別對python3做處理

======原本我用以下連結,但是他不會work,請不要用這方法了(Do NOT use this link)======
https://github.com/rainyear/lolita/issues/18

後來就使用anaconda虛擬環境來安裝又快又方便,是參考以下連結
https://github.com/twtrubiks/FaceDetect/tree/master/How%20Easy%20Install%20OpenCV%20%20for%20Python%20use%20Anaconda

安裝anaconda

1.下載檔案
https://conda.io/docs/user-guide/install/linux.html
2.執行(重要:最後結束時記得寫yes再跳出,他默認是no)

bash Anaconda-latest-Linux-x86_64.sh

3.重新整理~/.bashrc

source ~/.bashrc

先前的文章都是用手自己安裝
http://shamangary.logdown.com/posts/773013-install-torch7-cuda-cudnn-nvidia-driver

除了driver我還是推薦自己去下載安裝之外,其他像是cuda8.0和cudnn都不需要再像以前一樣麻煩了,
直接去anaconda搜尋版本安裝即可,一行搞定。

在anaconda環境安裝各種東西

建造一個名為cv3的虛擬環境,在裡面放入python3和opencv3

$conda create -n cv3 python=3.5.2
$source activate cv3
(cv3)$
(cv3)$conda install -c https://conda.anaconda.org/menpo opencv3
(cv3)$source deactivate
$

就是這麼簡單!!!!!!!請使用anaconda去安裝python3和opencv3!!!!!!!!!

當然還是要check

(cv3)$python
>>>import cv2
>>>cv2.__version__

如果要解除安裝的話就用以下

(cv3)$conda uninstall opencv3

注意自己安裝的代號是opencv3還是單純opencv,這會影響版本

接下來,如果你還需要dlib甚至是keras,甚至是tensorflow,pytorch怎麼辦捏!?

$source activate cv3
(cv3)$
(cv3)$conda install -c menpo dlib 
(cv3)$conda install -c conda-forge keras 
(cv3)$conda install -c anaconda tensorflow-gpu
(cv3)$conda install pytorch torchvision cuda80 -c soumith

就是這麼簡單!!!!!!!請愛用anaconda!!!!!!!!

注意如果使用pip安裝tensorflow是要選擇版本(python版本和gpu/cpu)的,請參考官方連結
https://www.tensorflow.org/install/install_linux

開啟和關閉環境

$source activate cv3
(cv3)$
(cv3)$source deactivate
$

刪除環境

$conda remove --name <the name> --all

Torch Tricks about 'cudnn', 'output size', and 'clearState()' with 'model size' (Torch 小技巧)

| Comments

1. 我的電腦只有CPU而沒有GPU,cudnn和nn能互相轉換嗎?

可以轉換,不過你要先安裝cudnn才可以,然而你通常需要有GPU的電腦才能安裝cudnn......你可能需要請你朋友幫忙:P

require('nn')
require('cudnn')

cudnn.convert(model,nn)       --=> convert to nn
cudnn.convert(model,cudnn)    --=> convert to cudnn

2. 我在跑我的model,但我不太想去算某一層的feature map size是什麼?有沒有辦法告訴我?

有,直接插入一層

model:add(nn.Reshape(1))

因為nn.Reshape()之中有內建在錯誤訊息中告訴你input和output的大小分別是多少,你跑一次後看錯誤訊息後知道大小了,就可以拔掉這層然後加入正確的下層參數了,例如說你的下層可能是nn.Linear(inputSize,outputSize)需要知道上一層的大小才能填入正確的inputSize,這個時候就可以用這個方法來處理了。

注意一件事情,nn.View()和nn.Reshape()能做的事情非常像,但是nn.View()沒有這個功能,所以我通常都用nn.Reshape()。

3. 我想縮小我的model,我已經用了clearState()除掉了暫存的output(也就是feature maps),為何clearState()沒有處理gradWeight和gradBias?

https://github.com/soumith/cudnn.torch/issues/138
理論上來說,如果你做完training了,應該是不需要gradient了,因為你只需要做forward得到結果而已,但是通常我們有時候會想要繼續在已知的model做training,是故如果沒有留下gradWeight和gradBias,之後會很麻煩,所以作者設計上不希望clearState()拿掉它們。

然而有時候我們真的不需要它們兩個,有沒有辦法去除?有人幫寫了
https://github.com/fmassa/optimize-net
乖乖膜拜大神就好(祭拜中

4. 為何我用了clearState()後我的model還是超級大

https://github.com/soumith/cudnn.torch/issues/313
有可能是因為你之前是用cudnn去做training,之後你轉回了nn,然而你發現在nn底下的model就算做了clearState(),容量還是超級大,這是因為好像最新的cudnn加入了input_slice和output_slice這樣的變數,而在nn底下沒辦法處理,是故你應該在cudnn底下做完clearState()後,再轉回nn即可(雖然我覺得這種事情根本就算是bug)。

5. (2017/07/24更新) 有沒有那種在nn有處理,但是cudnn沒有處理的clearState()?

答案是有的......例如說cudnn.SpatialMaxPooling就完全沒有寫clearState(),而在nn底下就有,
如果想要確保都有clear,建議你在cudnn底下做一次clearState(),回到了nn後記得再做一次(天啊。

6. (2017/07/24更新) 如何使用optnet?可以畫出使用記憶體大小的圖解嗎?

require('nn')
require('dpnn')
optnet = require('optnet')


---------------------------------------------
--Optimize the memory by optnet

net = torch.load('model_best.t7')

net:float()
print(net)
input = torch.rand(1,3,55,55):float()
opts = {inplace=true, mode='inference'}
optnet.optimizeMemory(net,input,opts)

net:clearState() -------------------------> You still need to do this...
torch.save('test.t7',net)

---------------------------------------------
--Plot the memory usage
generateGraph = require 'optnet.graphgen'

-- visual properties of the generated graph
-- follows graphviz attributes
graphOpts = {
displayProps =  {shape='ellipse',fontsize=14, style='solid'},
nodeData = function(oldData, tensor)
  return oldData .. '\n' .. 'Size: '.. tensor:numel()
end
}

g = generateGraph(net, input, graphOpts)

graph.dot(g,'test','myTest')
--------------------------------------------

有一次執行失敗是說不要用'th',而要用qlua當作開頭,好像是畫圖需要

qlua compress.lua

但是我後來執行又發現'th'又可以了,不管,反正你就都試試看

th compress.lua

執行完後你會發現你會畫出'myTest.svg'的圖,同樣顏色代表同樣大小,不過好像沒有直接看處哪層比較大的方法,好像只能從相對關係看吧,如果想要縮小你的model就進去找找看有什麼東西是多存的。例如說如果你用了dpnn的東西,optnet未必能移除所有的多餘變數,是故這一步畫圖還蠻重要的。我畫出來才發現optnet似乎沒有移除SpatilMaxPooling的變數,所以要去check。

7. (2017/07/24更新) 用完optnet還需要用clearState()嗎?

竟然是需要的......總之我再用了一次後,竟然又變小了,不知道是殺小。

8. (2017/07/24更新) Summary of compress a torch model

總之下面步驟就是全做最保險

1. clearState() under cudnn

2. convert cudnn to nn

3. clearState() under nn

4. use optnet for inference model

5. clearState() under nn (again)

A Coding Level Note for Extending CNN to RNN/LSTM Implementation in Torch

| Comments

CNN training

在單張image處理時,我們在torch中CNN model的寫法通常是先建立

<<Network shell>>

model = nn.Sequential()

隨後再插入你想要的layers,注意到這裡我們在training時的tensor input,其維度是

<<Input tensor convention>>

input tensor(4 dim): batchsize x channel x width x height

值得注意的是,在CNN的設計中,batchsize的大小是多少比較無所謂,
是取決於你自己而已,然而後面三項會直接影響到你的CNN會不會發生錯誤,
因為每個layer中都會有嚴格對這三項的限制。

我們先看一下標準的CNN在training會需要哪些東西:

<<General CNN training process>>

Step.1  Preprocessing
---------------------------------------------------------------------------

<model or mlp (multi-layer perceptron) loading>

<criterion setting>

<optimState setting>

local w,dE_dw = model:getParameters()

---------------------------------------------------------------------------



Step.2 Training through epoch
---------------------------------------------------------------------------

for epoch = 1, maxEpoch do

    for t = 1, nBatches-1 do
        
        xlua.progress(t+1, nBatches) -- disp progress

        <inputs per batch setting> 

        local eval_E = function(w)
             
             <forward and backward propagation>

             return loss, dE_dw
        end

        optim.sgd(eval_E, w, optimState)

    end
end

---------------------------------------------------------------------------

可以發現torch設計的非常的精良,只要遵循這個流程基本上CNN的training
不會有太大的困難,大部分人對於CNN的改進設計主要是在

(1) model

(2) loss function

而這兩件事情通常可以在 Step.1 Preprocessing 就處理完大部分的設計,
其他小改動就不會影響到程式的整體架構,所以可以說還是相當清楚的。

我們再把其中propagation的地方寫清楚一點

<<Propagations>>

local eval_E = function(w)

    -- reset gradients 
    dE_dw:zero() 

    -- evaluate function for complete mini batch 
    local outputs = model:forward(inputs) 
    local loss = criterion:forward(outputs,targets) 

    -- estimate dE/dW 
    local dE_dy = criterion:backward(outputs,targets) 

    model:backward(inputs,dE_dy) 

    -- return E and dE/dW 
    return loss,dE_dw 

end

簡單來說就是從前到後:inputs -> outputs -> loss
再從後到前:outputs -> dE_dy -> dE_dw
就醬而已,沒惹 (´・ω・`),技術上來說數學他都幫你處理好了,
連sgd本身你甚至都不需要知道是什麼他就幫你update這樣。
(當然我們作研究還是要自己搞懂基礎喔^.<)

如果想要用GPU加速的話,記得要用

inputTensor:cuda()
model:cuda()

就是把東西移到GPU那裡去,才可以加速喔。

此外,如果為了避免爆炸可能會需要gradient clipping

dE_dw:clamp(-opt.gradClip,opt.gradClip)

這種的細節就是各種實驗中各有不同了。

參考資料:
PN-Net
Activity-Recognition-with-CNN-and-RNN

RNN/LSTM training

由於最近也在弄action recognition的東西,
其中我們實驗是的研究方向有一部份是LSTM的處理和改進,
所以我選擇了torch的RNN library作為實作的媒介,以下是採用的來源。
Github: https://github.com/Element-Research/rnn

在影片處理中,比影像處理多了一個維度:時間,
如果我們加入了時間維度,我們應該將tensor變成5維嗎?
這跟library的設計有關係,而我們想要處理的這個rnn library不是這樣處理的,
他將整個時間序列(time sequence)視為一個lua table,
也就是說我們處理的tensor的維度不變,但是每個時間點的image都被紀錄在同一個table中,
假設我們已經通過了某幾層的CNN,而每張圖都可以用2048x7x7的feature map來表示,
那麼一段影片包含了好幾個幀,也就是好幾張圖片的特徵可以用下面的table來表示。

Input sequence split list.
(batchsize=2, channel=2048, w=7, h=7, numSequence=10)
{
  1 : CudaTensor - size: 2x2048x7x7
  2 : CudaTensor - size: 2x2048x7x7
  3 : CudaTensor - size: 2x2048x7x7
  4 : CudaTensor - size: 2x2048x7x7
  5 : CudaTensor - size: 2x2048x7x7
  6 : CudaTensor - size: 2x2048x7x7
  7 : CudaTensor - size: 2x2048x7x7
  8 : CudaTensor - size: 2x2048x7x7
  9 : CudaTensor - size: 2x2048x7x7
  10 : CudaTensor - size: 2x2048x7x7
}

而RNN或LSTM說穿了也是CNN的一種而已,只不過是比較複雜一點,
所以接下來的問題是,如何使用CNN來直接處理(平行或是有相互關係的)時間序列?
Element-Research給我們的答案是他們創造了一個新的模式

model = nn.Sequencer()

可以想像成,input table中的每個element,也就是每個tensor,
會經過一樣的CNN處理,如果中間有RNN連結時間之間的關係的話,
似乎會連結彼此的時序關係。

其中他們設計的rnn library有幾個比較需要注意的地方,是之前處理CNN沒有遇到的。

model:remember()
model:training()
model:forget()
model:evaluate()

我們看一下他們的定義

training()

In training mode, the network remembers all previous rho (number of time-steps) states. This is necessary for BPTT.

evaluate()

During evaluation, since their is no need to perform BPTT at a later time, only the previous step is remembered. This is very efficient memory-wise, such that evaluation can be performed using potentially infinite-length sequence.

remember([mode])

When mode='neither' (the default behavior of the class), the Sequencer will additionally call forget before each call to forward. When mode='both' (the default when calling this function), the Sequencer will never call forget. In which case, it is up to the user to call forget between independent sequences. This behavior is only applicable to decorated AbstractRecurrent modules. Accepted values for argument mode are as follows :

'eval' only affects evaluation (recommended for RNNs)
'train' only affects training
'neither' affects neither training nor evaluation (default behavior of the class)
'both' affects both training and evaluation (recommended for LSTMs)

forget(offset)

This method brings back all states to the start of the sequence buffers, i.e. it forgets the current sequence. It also resets the step attribute to 1. It is highly recommended to call forget after each parameter update. Otherwise, the previous state will be used to activate the next, which will often lead to instability. This is caused by the previous state being the result of now changed parameters. It is also good practice to call forget at the start of each new sequence.

裡面有一句話:
Note : make sure you call brnn:forget() after each call to updateParameters().
就是每次你update完之後記得要forget()一下。

由於在原本Element-Research他們的github我總覺得看的不是很清楚,
所以我看了一下ConvLSTM他們的實作,
有幾種情況的組合如以下所示:

在建立model後:

model:remember('both')
model:training()

在training進行之前(還沒進入epoch的for loop):

model:training()
model:forget()

在training進行之中(已進入epoch的for loop,每次epoch optim update結束之後):

model:forget()

在evaluation開始之前執行一次:

model:evaluate() 
model:forget()

簡單來說,一旦你執行了forward propagation不管是在training或testing,
你的time step可能就不是初始值了,而這樣再下一次要再propagation時似乎會造成不穩定,
所以都要記得forget()一下這樣。

至於training()和evaluate()就是前者需要記得所有的時間點,
後者只是為了一次的測試而已,不需要記這麼多消耗資源。

而remember('both')是說forget()他內部不會去call,
是完全讓使用者自己決定什麼時候call forget()這樣。
(雖然好像沒有明確寫出remember()和training()之間的差別就是了,
不過他好像兩個都會call一次,以後更懂了再更新。)

除此之外幾乎跟CNN的training是一模一樣的。

OpenFace Installation/Setup by Hand (安裝OpenFace)

| Comments

Target(目標):

The post is the setup guildeline of the OpenFace face recoginition sample code. Due to the development of the GPU and the Cuda version, the original installation guideline is not working for my computer. Therefore I record how did I setup the OpenFace step-by-step in order to save the time for the next people who need this.

由於在2016年之前,對於face recoginition這個問題已經有許多方法效果顯示為極好(Deepface,FaceNet),然而他們使用的training dataset都是私人的,並且可能不是寫在torch的介面上,是故我們torch7的使用者們來使用開源的OpenFace是個合適的選擇。然而,由於硬體和支援的軟體(GPU,Cuda)每年都在發展,是故不同的硬體可能不適用他的安裝方式,在此我記錄下自己的安裝方式以供之後的人參考。

OpenFace:

Home:
https://cmusatyalab.github.io/openface/
Setup: (This is not working for my environment. View on 2016/8/23)
https://cmusatyalab.github.io/openface/setup/

Environment(環境):

Ubuntu 14.04
Nvidia GTX 1070
Cuda 8.0

Required Library(必須的資料庫):

OpenCV

安裝OpenCV一直都是很麻煩的一件事情,特別是在你用了最新的GPU後很有可能舊的安裝方法或版本就不適用了,這裡記錄一下成功和失敗的版本。

Fail: OpenCV 2.4.11, OpenCV on the github (View on 2016/8/23)
Success: OpenCV 2.4.13

官網直接推薦你使用2.4.11,然而你會發現這跟Cuda 8.0不和,如果直接用github上的OpenCV也會不和,必須直接去OpenCV的官網下載2.4.13才能使用,估計大概是因為2016/5/19才更新的2.4.13有特別針對1070和Cuda 8.0做優化(這講起來也很合理,這硬體其實出現沒有多久)

選對了版本後安裝就按照官網的方式就很簡單了
http://docs.opencv.org/2.4/doc/tutorials/introduction/linux_install/linux_install.html

[compiler] sudo apt-get install build-essential
[required] sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
[optional] sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

cd ~/opencv
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install

FFmepg

(Openface doesn't need this one, but I put it here for convenient.)
http://www.j4.com.tw/comp-qna/ubuntu-install-ffmpeg-and-obs-studio/
http://www.linuxquestions.org/questions/programming-9/lavdevice-not-found-4175441900/

Dlib

Fail: dlib-18.16
Sucess: dlib-19.1.0

https://pypi.python.org/pypi/dlib
由於直接使用OpenFace官網提供的我也無法成功,然而我去下載19.1.0之後按照他們網頁的安裝也無法成功,成功的是下載19.1.0後按照OpenFace的安裝方法安裝才成功(WTF)。

下載dlib-19.1.0並解壓縮下載的檔案後,執行下面動作,但會發現缺少Boost

cd dlib-19.1.0/python_examples
mkdir build
cd build
cmake ../../tools/python
cmake --build . --config Release
sudo cp dlib.so /usr/local/lib/python2.7/dist-packages

還好19.1.0裡面把怎麼安裝boost都寫好了,error一跑出來就知道怎麼修正,是故按照下面處理後,再回去上面安裝dlib即可

-- Could NOT find Boost
--  *****************************************************************************************************
--  To compile Boost.Python yourself download boost from boost.org and then go into the boost root folder
--  and run these commands: 
--     ./bootstrap.sh --with-libraries=python
--     ./b2
--     sudo ./b2 install
--  *****************************************************************************************************

Checking OpenCV and Dlib

如果上面都處理完了我們來確認一下是否有正確安裝好,OpenFace的官網也提供好了確認的方法

python2
import cv2
import dlib

---------2017/10/02更新-------------
如果遇到cv2 no module named cv2的話,用下面指令安裝即可

pip install opencv-python

如果什麼問題都沒有大概你就是裝好囉(^.<)V,我是說需要的資料庫而已

Torch7

接下來Torch本身也是有許多需要的東西的,官網提供如下

dpnn
nn
optim
csvigo
cutorch and cunn (only with CUDA)
fblualib (only for training a DNN)
tds (only for training a DNN)
torchx (only for training a DNN)
optnet (optional, only for training a DNN)

其中比較常見的是(nn, cutorch, cunn)因為一般的CNN和程式都需要用到這三個
雖然呢雖然,官網說可以用以下命令一次全部裝完

**Warning: DO NOT USE THE FOLLOWING COMMAND**

for NAME in dpnn nn optim optnet csvigo cutorch cunn fblualib torchx tds; do luarocks install $NAME; done

但是呢我用了一下torch就整個爆掉了呢呵呵呵呵呵呵呵呵呵(╯-_-)╯ ~╩╩
所以我又刪掉了整個torch重裝了一次◢▆▅▄▃ ╰(ʘдʘ╬)╯ ▃▄▅▆◣

還好torch是很親民的,刪也簡單灌也簡單,Torch7的官網寫的很清楚(由此可知好的開源維護是多麼重要)
http://torch.ch/docs/getting-started.html#_

回過頭來,審視了一下OpenFace的code,估計八成是因為cutorch和cunn不太能重裝(我也不知道為什麼),所以我們拿掉已經有的(nn, cutorch, cunn)這三者再試一次
2017/07/18更新:有時候重新安裝torch的時候會失敗,也是因為cutorch等等的問題,原因是當torch更新的時候,他們很可能會需要新的cuda或是cudnn,這時候你可能就要先更新他們,你再安裝torch就不會有問題了。

for NAME in dpnn optim optnet csvigo fblualib torchx tds; do luarocks install $NAME; done

這樣就安裝成功了,並且沒有出現問題,是故我們以後在安裝其他東西的時候要小心不要動到cutorch或cunn之類的東西會比較好。

順帶一提只有STN在安裝的時候比較麻煩一點,但在OpenFace中並不需要我們只是順便一提而已

Install STN
luarocks install https://raw.githubusercontent.com/qassemoquab/stnbhwd/master/stnbhwd-scm-1.rockspec

OpenFace

安裝前置蠻簡單的

sudo python2 setup.py install
sh models/get-models.sh

順帶一提,他們官網的很多執行建議都沒有寫前標,例如說python2和th等等,可能是因為他在哪裡有執行某種環境變數,但我在這就是都寫上去就是。

Dataset

在開始之前,我們先說明之後會用到的比較大需要下載的dataset,下面自動和手動的都要下載才行
自動下載小dataset:
注意前面要寫bash而不是sh(我也不知道為什麼)

bash ./data/download-lfw-subset.sh

手動下載大dataset:
http://vis-www.cs.umass.edu/lfw/
我們下載前兩個(在網頁中要找一下):

#lfw:(put the content of this one into ./data/lfw/raw)
All images as gzipped tar file
(173MB, md5sum a17d05bd522c52d84eca14327a23d494)

#lfw-deepfunneled:(put the content of this one into ./data/lfw/deepfunneled)
[new] All images aligned with deep funneling 
(111MB, md5sum 68331da3eb755a505a502b5aacb3c201)

還有pairs.txt的資訊也要手動下載,放到./data/lfw/的底下去
http://vis-www.cs.umass.edu/lfw/pairs.txt

上面四個下載完後,需要解壓縮的就解壓縮,然後都放到./data/底下,所以會有下面的資料夾和檔案

./data/lfw/raw
./data/lfw-subset
./data/lfw/deepfunneled
./data/lfw/pairs.txt

Demo

很奇怪的是有幾個需要web的demo我通通都無法執行,上網查了目前還無法解決。

Demo3倒是可以執行
http://cmusatyalab.github.io/openface/demo-3-classifier/
首先Demo3最下面的"Minimal Working Example to Extract Features"不用下載lfw-subset也可以執行

##Minimal Working Example to Extract Features
#command (remember to add "python2" and "th")
mkdir -p classify-test/raw/{lennon,clapton}
cp images/examples/lennon-* classify-test/raw/lennon
cp images/examples/clapton-* classify-test/raw/clapton
python2 ./util/align-dlib.py classify-test/raw align outerEyesAndNose classify-test/aligned --size 96
th ./batch-represent/main.lua -outDir classify-test/features -data classify-test/aligned

#warning
OpenBLAS Warning : Detect OpenMP Loop and this application may hang. Please rebuild the library with USE_OPENMP=1 option.

#output
nImgs:  4
Represent: 4/4

#How to fix the warning?
-- Add this line to the function "repBatch()" inside "./batch-represent/batch-represent.lua"
torch.setnumthreads(1)

上面還是要注意加上python2和th就可以執行了,執行過程會出現warning是OpenBLAS和OpenMP的問題,不過似乎還是可以執行完成,這個原因似乎是因為torch某次更新把USE_OPENMP=1這個flag給移除了。
https://github.com/torch/ezinstall/issues/26
藉由限制thread的數目我們也可以解決這個問題了。

Preprocessing

接下來我們看一下Demo3和Training new neural network models都有的前處理
http://cmusatyalab.github.io/openface/demo-3-classifier/
1. Create raw image directory
關於dataset,在Minimal Working Example to Extract Features中使用的只是幾張images,這裡我們對之後會使用的dataset進行一個下載的動作。

2. Preprocess the raw images

#Change 8 to however many separate processes you want to run: 
for N in {1..8}; do python2 ./util/align-dlib.py <path-to-raw-data> align outerEyesAndNose <path-to-aligned-data> --size 96 & done

#Prune out directories with less than 3 images per class with:
python2 ./util/prune-dataset.py <path-to-aligned-data> --numImagesThreshold 3

Replace <path-to-raw-data> and <path-to-aligned-data> by the path of step.1

3. Generate Representations

th ./batch-represent/main.lua -outDir <feature-directory> -data <path-to-aligned-data>

看到這裡可以發現"Minimal Working Example to Extract Features"就包含了前三步,可以視為一個完整的範例,但是第四步開始就不一樣了。

4. Create the Classification Model

執行第四步開始出現問題

#command
python2 ./demos/classifier.py train ./classify-test/features

#error1
Traceback (most recent call last):
  File "./demos/classifier.py", line 34, in <module>
    import pandas as pd
ImportError: No module named pandas

#error2 (after installing pandas)
Traceback (most recent call last):
  File "./demos/classifier.py", line 38, in <module>
    from sklearn.pipeline import Pipeline
ImportError: No module named sklearn.pipeline

補安裝,缺少的有pandas、Scipy、scikit-learn
https://www.scipy.org/install.html
http://scikit-learn.org/stable/install.html

sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose
sudo pip install -U scikit-learn

安裝完之後第四步也能跑了喔!!會show出下面的東西就是跑成功了。

/usr/local/lib/python2.7/dist-packages/sklearn/lda.py:4: DeprecationWarning: lda.LDA has been moved to discriminant_analysis.LinearDiscriminantAnalysis in 0.17 and will be removed in 0.19
  "in 0.17 and will be removed in 0.19", DeprecationWarning)
Loading embeddings.
Training for 2 classes.
Saving classifier to './classify-test/features/classifier.pkl'

Classifying New Images
上面好了的話接下來也不會有問題

#command
python2 ./demos/classifier.py infer ./models/openface/celeb-classifier.nn4.small2.v1.pkl images/examples/{carell,adams,lennon}*

#output
/usr/local/lib/python2.7/dist-packages/sklearn/lda.py:4: DeprecationWarning: lda.LDA has been moved to discriminant_analysis.LinearDiscriminantAnalysis in 0.17 and will be removed in 0.19
  "in 0.17 and will be removed in 0.19", DeprecationWarning)

=== images/examples/carell.jpg ===
Predict SteveCarell with 0.97 confidence.

=== images/examples/adams.jpg ===
Predict AmyAdams with 0.81 confidence.

=== images/examples/lennon-1.jpg ===
Predict SteveCarell with 0.50 confidence.

=== images/examples/lennon-2.jpg ===
Predict DavidBoreanaz with 0.43 confidence.

Running The LFW Experiment

http://cmusatyalab.github.io/openface/models-and-accuracies/
接著進入最重要的部份,實作LFW的實驗,但是根據官網的流程走就可以了,還是記得自己加上python2和th之類的前標。

for N in {1..8}; do python2 ./util/align-dlib.py data/lfw/raw align outerEyesAndNose data/lfw/dlib-affine-sz:96 --size 96 & done

python2 ./util/align-dlib.py data/lfw/raw align outerEyesAndNose data/lfw/dlib-affine-sz:96 --size 96 --fallbackLfw data/lfw/deepfunneled

How to fix "out of memory" issue?
在執行下一個動作時我們需要注意兩點,
第一點,我們想要用cudnn加速,
第二點,我們想要使用很少的GPU記憶體(因為GTX1070只有8G的RAM),

首先我們到./batch-representation/opts.lua中把cuda的選項改成true
接下來我們改動./batch-representation/batch-representation.lua的code
加上一個require

require 'cutorch'

在repBatch()這個function中改寫如下

function repBatch(paths, inputs, labels, batchSz)
   batchNumber = batchNumber + batchSz
   -- number of threads
   torch.setnumthreads(1)
   --[[
   if opt.cuda then
      inputs = inputs:cuda()
   end
   --]]
   --print(inputs:size())

   if opt.cuda then
         embeddings  = torch.CudaTensor(inputs:size(1),128)
         local embeddingsSplit = embeddings:split(10)
         for j,v in ipairs(inputs:split(10)) do
            v_temp = v:cuda()
            embeddingsSplit[j]:copy(model:forward(v_temp))
         end
         embeddings = embeddings:float()
   else
         embeddings = model:forward(inputs):float()
   end
   
   if opt.cuda then
      cutorch.synchronize()
   end

   if batchSz == 1 then
      embeddings = embeddings:reshape(1, embeddings:size(1))
   end

   for i=1,batchSz do
      labelsCSV:write({labels[i], paths[i]})
      repsCSV:write(embeddings[i]:totable())
   end

   print(('Represent: %d/%d'):format(batchNumber, nImgs))
end

注意為何要如此改寫,因為直接把inputs轉成cuda會需要很多的memory,因此我們不要用inputs:cuda(),
而且我們也不想要直接使用forward,我們把最後的embeddings切成很多份(10份),每份分開作forward,
(這種切法是參考pnnet的evaluation.lua,他的code裡面真的很多東西可以學習)
並且只在每個iteration中把inputs的切塊v進行cuda(),也就是v_temp = v:cuda()
如此一來可以避免需要很多memory的問題

接著我們執行下一步就會又快又需要很少的GPU memory

th ./batch-represent/main.lua -outDir evaluation/lfw.nn4.small2.v1.reps -model models/openface/nn4.small2.v1.t7 -data data/lfw/dlib-affine-sz:96

Evaluation
執行之前發現有錯誤,是因為matplotlib的版本太舊只有1.3.1,然而好像只有1.4以上有支援style這個東西,是故我們先升級一下

sudo pip install --upgrade matplotlib

升級完的確認

#command
python2
import matplotlib
matplotlib.__version__

#output
'1.5.2'

接著我們就能執行後面了。

後來發現他在做evaluation的時候需要pairs.txt,但是他的預設路徑可能跟你放的不太一樣,因為你存放openface-master的地方可能跟他的路徑不同,是故我們用--lfwPairs指定pairs.txt的位置

#command
cd evaluation

python2 ./lfw.py nn4.small2.v1 lfw.nn4.small2.v1.reps --lfwPairs ../data/lfw/pairs.txt

#output
/usr/local/lib/python2.7/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
Loading embeddings.
  + Reading pairs.
  + Computing accuracy.
    + 0.9292
Plotting.

官網提供的數據如下

# https://cmusatyalab.github.io/openface/models-and-accuracies/
Model                     Accuracy          AUC
nn4.small2.v1 (Default) 0.9292 ± 0.0134    0.973
nn4.small1.v1           0.9210 ± 0.0160    0.973
nn4.v2                  0.9157 ± 0.0152    0.966
nn4.v1                  0.7612 ± 0.0189    0.853
FaceNet Paper (Ref)     0.9963 ± 0.009 not provided

可以看出0.9292是符合預期的結果的,注意這裡的AUC是指ROC curve下的面積,和accuracy是不同的。

Training new neural network models

好啦還有些東西沒用完我們再回來,
官網中特別提醒了training DNN來取feature和training classification model是兩件完全不同的事情,這要特別記得一下,因為這裡的DNN只有拿來取feature而已

接著我們來follow官網:https://cmusatyalab.github.io/openface/training-new-models/
然而他有漏掉一些步驟沒有說明清楚

注意,這個地方其實必須要下載caisa dataset和facescrub dataset是兩個不同的dataset,所以如果你直接複製lfw過去當做training就是在training和testing都用一樣的東西,實驗結果是無意義的。(meaningless)

--------------------2017/07/18更新(start)--------------------
Download CASIA (complex): 需要寄信給簽名並等待回覆

1. Go to
http://www.cbsr.ia.ac.cn/english/CASIA-WebFace-Database.html

2. Follow the steps

Download Instructions: 
To apply for the database, please follow the steps below:

(1)Download and print the document Agreement for using CASIA WebFace database
(2)Sign the agreement (The agreement must be signed by the director or the delegate of the deparmart of university. Personal applicant is not acceptable.)
(3)Send the agreement to cbsr-request@authenmetric.com
(4)Check your email to find a login account and a password of our website after one day, if your application has been approved.
(5)Download the CASIA WebFace database from our website with the authorized account within 48 hours.

Download facescrub (simple): 直接下載即可,要花一段時間

1. Download files from
https://github.com/faceteam/facescrub

2. Run the command
python download.py

這時候我感到有點困惑,因為facescrub和CASIA目錄下的命名方式和lfw是很不一樣的,例如說CASIA解壓縮後的資料夾其實都不是人名,而是數字,而facescrub底下人名資料夾的裡面,每張圖的命名幾乎是亂碼而不是按照數字編號,這樣我懷疑是否會影響到標準處理步驟?如果他們沒有直接用到名字的話,估計是可以的......吧。

我們接下來follow標準流程

#Facescrub dataset preprocessing
for N in {1..8}; do python ./util/align-dlib.py data/facescrub/download align outerEyesAndNose data/facescrub/dlib-affine-sz:96 --size 96 & done

#CASIA dataset preprocessing
for N in {1..8}; do python ./util/align-dlib.py data/CASIA/CAISA-WebFace align outerEyesAndNose data/CASIA/dlib-affine-sz:96 --size 96 & done

=====2017/08/08更新=====
上面的cmd雖然沒錯,但是因為CASIA dataset太大了,會導致遍歷全部資料夾的os.walk失效,以下方法似乎可以修正(不過似乎要電腦有SSD讀檔比較快才會開始跑,不然好像很慢甚至無法跑)

cd data/CASIA

for N in {1..8}; do python ../../util/align-dlib.py ./CAISA-WebFace align outerEyesAndNose ./dlib-affine-sz:96 --size 96 & done

此外注意因為有可能下載或是複製錯誤,要記得去除一些空的資料夾,或是圖片數目少於3的,下面一段有指示。
=====2017/08/08更新=====

第一個資料夾是你放下載下來並解壓縮圖檔的資料夾,
第二個資料夾是你想要output結果的地方,
注意我們把他們前處理後的images通通都放到"./data/casia-facescrub/dlib-affine-sz:96"底下
(總覺得很奇怪官網怎麼沒有這方面的說明(=A=)??)
我上面的程式碼是先分別放到自己下面,因為其實兩個都要執行很久,難保不會出現錯誤,
都處理完之後再把裡面的subfolders複製到"./data/casia-facescrub/dlib-affine-sz:96"底下就好。

然後我看到了這個連結https://github.com/cmusatyalab/openface/issues/6
他們說基本上像是Megaface這種沒有label的dataset在openface是無法training的,然而CASIA和facescrub沒有共通的命名方式,換句話說,只要有資料夾名稱,每個資料夾能把不同的人分開,應該就是openface所需要的label了。

接著到./data/casia-facescrub/的目錄看到remove-lfw-names.py,這個檔是為了移除跟lfw相同的名稱的資料夾,以避免training和testing有重複的情形,我們修改裡面的一行,就是對應的資料夾名稱而已

# data/casia-facescrub/remove-lfw-names.py
# line10
names = os.listdir('dlib-affine-sz:96')

你想命名什麼都可以,就是在執行這個檔的時候要記得用一樣的名字才能執行。

cd data/casia-facescrub/
python remove-lfw-names.py

接著來移除檔案夾裡面太少圖片的地方:

python ./util/prune-dataset.py data/casia-facescrub/dlib-affine-sz:96 --numImagesThreshold 3

--------------------2017/07/18更新(end)--------------------

Train the model

原本以為苦難已經過去了,但是沒想到還是持續著
根據官網指示接下來只要先調整 ./training/opt.lua裡面的參數,然後執行th ./training/main.lua即可,然而會發現有些問題

安裝缺少的函式庫

luarocks install graphicsmagick
sudo apt-get install graphicsmagick
sudo apt-get install libgraphicsmagick-dev
sudo apt-get update

由於我用的還是不是預設路徑,所以要自己設定

cd ./training
th ./main.lua -data ../data/casia-facescrub/dlib-affine-sz:55/

上面雖然是正確的指令,但是有些原始的code的小錯誤必須修正後才能跑

2017/07/18更新:training可能會發生錯誤,例如說你想用55x55的input但是改了opts.lua還是得到96x96的,原因是因為training也有cache,要把training/work/trainCache.t7刪掉重跑就可以了

下面在require的時候不知道為什麼一定要用file的方式才能work

# training/train.lua
# line23
local model = require './model.lua'
# line24
local openFaceOptim = require './OpenFaceOptim.lua'

然後記得下載並解壓縮https://github.com/Atcold/torch-TripletEmbedding/tree/742599ce761b2d6cf128068b8f6fd327ad633a31
把東西放到./training/torch-TripletEmbedding下面才可以,直接去openface下載好像不行,一定要去原本作者的github下載再放到這裡才行。

另外,在training/test.lua中的兩個command也是沒有前標的,記得去line50和line57加上去th和python2,並且記得指定pairs.txt的位置。

cmd = 'th '.. cmd .. ' -batchSize ' .. opt.testBatchSize ..
      ' -model ' .. latestModelFile ..
      ' -data ' .. opt.lfwDir ..
      ' -outDir ' .. outDir ..
      ' -imgDim ' .. opt.imgDim
   os.execute(cmd)

   cmd = 'python2 '.. lfwEval .. ' Epoch' .. epoch .. ' ' .. outDir .. ' --lfwPairs /media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/data/lfw/pairs.txt'
   os.execute(cmd)

「必須!」為了要testing,改變在opts.lua中的lfwDir

cmd:option('-lfwDir', '/media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/data/lfw/dlib-affine-sz:96', 'LFW aligned image directory for testing.')

由於就算改了這項你的cache還是可能是錯的,所以執行之前我們要到./data/lfw/把裡面的cache.t7刪掉才行(如果你可以正確執行的話就不用了)

另外在./training/test.lua中有蠻多修正的,我就直接把他的function test()貼出來,其中像是不要加上-cuda,因為我們直接在./batch-representation/opts.lua中已經改成true了,還有在command上加上pairs.txt的絕對路徑位置,還有他原始code的cmd並沒有擷取正確的model,要改成latestModelFile,總之下面就是我用的最後版本

function test()
   if opt.cuda then
      model = model:float()
   end
   local latestModelFile = paths.concat(opt.save, 'model_' .. epoch .. '.t7')
   local outDir = paths.concat(opt.save, 'lfw-' .. epoch)
   print(latestModelFile)
   print(outDir)
   local cmd = batchRepresent
   if opt.cuda then
      assert(opt.device ~= nil)
      cmd = cmd .. ' -device ' .. opt.device .. ' '
   end
   cmd = 'th '.. cmd .. ' -batchSize ' .. opt.testBatchSize ..
      ' -model ' .. latestModelFile ..
      ' -data ' .. opt.lfwDir ..
      ' -outDir ' .. outDir ..
      ' -imgDim ' .. opt.imgDim
   print(cmd)
      
   os.execute(cmd)

   cmd = 'python2 '.. lfwEval .. ' ' .. latestModelFile .. ' ' .. outDir .. ' --lfwPairs /media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/data/lfw/pairs.txt'
   print(cmd)
   --os.exit()
   os.execute(cmd)

   lfwAcc = getLfwAcc(paths.concat(outDir, "accuracies.txt"))
   testLogger:add{
      ['lfwAcc'] = lfwAcc
   }
end

雖然可以跑了(有accuracy出來),但是會發現圖沒有畫出來,是因為evaluation的comparisons路徑沒有給對,我們把它改成絕對路徑,下面幾項是在./evaluation/lfw.py中的路徑,根據你的資料夾路徑而改

# absolute path of the comparisons file folder in ./evaluation/lfw.py

openbrData = pd.read_csv("/media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/evaluation/comparisons/openbr.v1.1.0.DET.csv")
    
humanData = pd.read_table(
        "/media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/evaluation/comparisons/kumar_human_crop.txt", header=None, sep=' ')
    
deepfaceData = pd.read_table(
        "/media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/evaluation/comparisons/deepface_ensemble.txt", header=None, sep=' ')
    
eigData = pd.read_table(
        "/media/tsunyi/0EF057F8F057E50D/codeDemo/openface-master/evaluation/comparisons/eigenfaces-original-roc.txt", header=None, sep=' ')  

此外為了畫出來的圖漂亮,我們也修改一下./evaluation/lfw.py中的畫圖選項,新增一個epoch_tag當做畫圖的label

epoch_tag = tag.split('/')
epoch_tag = epoch_tag[-1] # choose the last element to be the label
ax.legend([humanPlot, dfPlot, brPlot, eigPlot,
           meanPlot, foldPlot],
          ['Human, Cropped [AUC={:.3f}]'.format(humanAUC),
           # 'Baidu [{:.3f}]'.format(baiduAUC),
           'DeepFace Ensemble [{:.3f}]'.format(deepfaceAUC),
           'OpenBR v1.1.0 [{:.3f}]'.format(brAUC),
           'Eigenfaces [{:.3f}]'.format(eigAUC),
           'OpenFace {} [{:.3f}]'.format(epoch_tag, AUC),
           'OpenFace {} folds'.format(epoch_tag)],
          loc='lower right')

我們再執行一次

th ./main.lua -data ../data/casia-facescrub/dlib-affine-sz:96/

發現一切都可以跑了,我的天啊真的是超麻煩。

如果training遇到困難,你可能要check幾個點:
1.有沒有改寫上面的path,將你自己的path填入正確的地方
2.執行錯誤可能跟cache有關,例如說刪除./data/lfw/dlib-affine:96/cache.t7重新執行後說不定就可以了

接下來至少我執行就沒有問題了.......(倒地
如果你發現了其他問題可以寄信或在下面留言補充。

==================2017/07/23更新(start)==================
1.在training過程中發現錯誤,少了某個人的資料夾,執行batch-representation時錯誤

可能是因為dlib無法正確對應到人臉的landmark進行align所以忽略了某個人,這時候要執行

python ./util/align-dlib.py data/lfw/raw align outerEyesAndNose data/lfw/dlib-affine-sz:96 --size 96 --fallbackLfw data/lfw/deepfunneled

其中fallbackLfw的意思是當dlib執行錯誤時,用deepfunneled中的檔案取代。
這錯誤可能就是你當初少執行了這一行這樣。

2.為何儲存下來的model超級大??以前好像不會

這是因為openface的寫法是先轉換model到nn,再進行clearState(),詳情請參考以下:
http://shamangary.logdown.com/posts/2067901

==================2017/07/23更新(end)==================

Analysis of CNN Architecture. Using STN+PNNET, Residual Network as Examples.

| Comments

Introduction

在使用Torch7來編寫你自己的CNN架構時,我們需要先了解一下每個單位所負責的事情,就CNN的結構來說,大家通常只知道weight filter還有feature map之類的東西,如何排序整個架構在paper上是可以用箭頭和長方形來做high level的解說,但你在寫程式總不能這樣寫吧(好啦或許有些程式可以,但是我們可愛的Torch7不行),所以我們要從小到大再從大到小來看一下如何拆解和理解整個架構,我們用的範例是STN(Spatial Transformaer Network, https://github.com/qassemoquab/stnbhwd)+PNNET(PN-Net, https://github.com/vbalnt/pnnet),並在最後我們討論一下如何寫residual network (https://github.com/gcr/torch-residual-networks)

Problem definition

我們現在想處理的問題是local descriptor,也就是

input=32x32 patch --> CNN --> local descriptor=128 dim real-valued vector

本篇的重點是如何建立該CNN的model,一旦建立好了該model,你便可以使用該model去做例如Siamese network的training,本篇先不著墨如何寫training的lost function code,而先著重分析該model的每個部份為何如此寫。

對於local descriptor來說,在[arXiv16] LIFT 中明確指出有了STN的加持,在train你的siamese network時可以為你的descriptor加上一層transformation的保護,進而讓你產生出來的descriptor有對transformation更好的抵抗能力,是故本篇使用STN作為前導,在model的一開始先矯正input patch的transformation,並使用[arXiv16] PNNET作為local descriptor的network,藉以產生128-dim的向量。

STN+PNNET Model

nn.Sequential {
  [input -> (1) -> (2) -> (3) -> (4) -> (5) -> (6) -> (7) -> (8) -> (9) -> output]
  (1): nn.Sequential {
    [input -> (1) -> (2) -> (3) -> output]
    (1): nn.ConcatTable {
      input
        |`-> (1): nn.Sequential {
        |      [input -> (1) -> (2) -> output]
        |      (1): nn.Identity
        |      (2): nn.Transpose
        |    }
        |`-> (2): nn.Sequential {
        |      [input -> (1) -> (2) -> (3) -> (4) -> (5) -> (6) -> (7) -> (8) -> (9) -> (10) -> (11) -> (12) -> output]
        |      (1): cudnn.SpatialMaxPooling(2x2, 2,2)
        |      (2): cudnn.SpatialConvolution(1 -> 20, 5x5)
        |      (3): cudnn.ReLU
        |      (4): cudnn.SpatialMaxPooling(2x2, 2,2)
        |      (5): cudnn.SpatialConvolution(20 -> 20, 5x5)
        |      (6): cudnn.ReLU
        |      (7): nn.View(80)
        |      (8): nn.Linear(80 -> 20)
        |      (9): cudnn.ReLU
        |      (10): nn.Linear(20 -> 6)
        |      (11): nn.View(2, 3)
        |      (12): nn.AffineGridGeneratorBHWD
        |    }
         ... -> output
    }
    (2): nn.BilinearSamplerBHWD
    (3): nn.Transpose
  }
  (2): cudnn.SpatialConvolution(1 -> 32, 7x7)
  (3): cudnn.Tanh
  (4): cudnn.SpatialMaxPooling(2x2, 2,2)
  (5): cudnn.SpatialConvolution(32 -> 64, 6x6)
  (6): cudnn.Tanh
  (7): nn.View(4096)
  (8): nn.Linear(4096 -> 128)
  (9): cudnn.Tanh
}

如果你第一次接觸會覺得很混亂,但是概念上我們可以把上表表示成下面的形式

nn.Sequential {
  (1): nn.Sequential {
    **STN**
  }
  **PNNET**
}

基本上就是STN先,然後再做PNNET的local descriptor。

STN (Spatial Transformer Network)

同樣我們先把上面STN複雜的形式改成概念上的說明,如下表所示

  (1): nn.Sequential {
    [input -> (1) -> (2) -> (3) -> output]
    (1): nn.ConcatTable {
      input
        |`-> (1): first branch is there to transpose inputs to BHWD, for the bilinear sampler
        |    
        |`-> (2): second branch is the localization network
        |    
         ... -> output
    }
    (2): nn.BilinearSamplerBHWD
    (3): nn.Transpose
  }

首先第一個需要注意的地方就是,這裡使用了nn.ConcatTable,我們先看一下Torch7對他的定義
https://github.com/torch/nn/blob/master/doc/table.md

# module = nn.ConcatTable()
                  +-----------+
             +----> {member1, |
+-------+    |    |           |
| input +----+---->  member2, |
+-------+    |    |           |
   or        +---->  member3} |
 {input}          +-----------+

也就是你定義的不同member都會拿到同樣的input,而ouput也是table,會直接作為下一個module的input輸入過去。

我們在STN中所看到的input就是我們的32x32的image patch,而在標準的STN中有兩個member

input --> 32x32 image patch

member1--> 把input轉成BHWD形式

member2--> 找出transformation的grid

// Bilinear sampling is done in BHWD (coalescing is not obvious in BDHW)
B:batch, D:dimension, H: height, W:width

一旦我們得到了grid還有input的BHWD形式,我們就可以用這兩者產生轉換後用bilinear產生的output也是32x32的patch,但是是經過STN轉正過的。

特別有用的是,在**nn.BilinearSamplerBHWD**這層接收到的input是一個table,
也就是上面提到的兩個東西,但是ouput卻是一個而已,也就是說,
這種寫法對於設計CNN model是非常有用的,必須要記起來。
具體來說如何去寫,就要參考BilinearSamplerBHWD.lua裡面的東西了。

Others

既然我們都看到了nn.ConcatTable,我們也順便看一下其他支援的形式

# module = nn.ParallelTable()
+----------+         +-----------+
| {input1, +---------> {member1, |
|          |         |           |
|  input2, +--------->  member2, |
|          |         |           |
|  input3} +--------->  member3} |
+----------+         +-----------+

其他還有太多種就去官網看吧 --> https://github.com/torch/nn/blob/master/doc/table.md

Residual network

類似於nn.ConcatTable的寫法,我們特別來參考一下residual network要怎麼寫才對
https://github.com/gcr/torch-residual-networks/blob/master/residual-layers.lua

# Concept
               Input
                 |
         ,-------+-----.
   Downsampling      3x3 convolution+dimensionality reduction
        |               |
        v               v
   Zero-padding      3x3 convolution
        |               |
        `-----( Add )---'
                 |
              Output
              
# How to add them up? (Only show the important part)

   local skip = input

   -- Add them together
   net = cudnn.SpatialBatchNormalization(nOutChannels)(net)
   net = nn.CAddTable(){net, skip}
   net = cudnn.ReLU(true)(net)

其中的關鍵就是nn.CAddTable()這個方法,懂了之後想要怎麼設計都行。

至於怎麼插入這樣的東西到你的model中去,可以參考 https://github.com/gcr/torch-residual-networks/blob/master/train-cifar.lua,他把它直接寫成了一個layer可以很方便的插入

model = addResidualLayer2(model, 32, 64, 2)

由此可知,實行elementwise的運算在Torch7中也是有支援的,我們特別將他們列出來因為特別有用

# CMath Modules perform element-wise operations on a table of Tensors:

CAddTable: addition of input Tensors;
CSubTable: substraction of input Tensors;
CMulTable: multiplication of input Tensors;
CDivTable: division of input Tensors;

Conclusion

本篇文章中我們討論了如何去看還有如何去寫你所想要的CNN結構,之前想寫residual的概念但是不太懂程式上怎麼搞,概念上懂了之後我們就能迅速的在Torch7上面開發了。

安裝 Installation of Torch7, Cuda, Cudnn, Nvidia Driver with GTX1070

| Comments

GPU: Nvidia GTX1070 Platform: Ubuntu 14.04 LTS

經過了兩天的掙扎,終於把這一切都搞定了。 從頭開始條列問題

1. Nvidia GTX1070使用hdmi線的話,ubuntu14.04會有頻率out of range(超出工作範圍)的問題

在Grub模式(選擇要進入體驗ubuntu,安裝ubuntu,或windows的黑屏畫面),如果直接按enter選擇安裝ubuntu,你會連安裝都無法,因為直接黑畫面,頻率好像只有30幾螢幕不支援,這時候你想安裝什麼driver都無法,

(a)你同時有windows和ubuntu:解決方法是不要按enter,先上下選擇ubuntu(反白為選擇而不是按enter)並按下e,會進入編輯模式,
(b)你只有ubuntu:開機之後就按e按到進入編輯模式

然後找到"linux......quiet splash......"這行,並改寫為:

quiet splash nomodeset

,然後按下F10啟動,這時候你就進的去了,

但是要注意這是暫時的,每次重開不可能都這樣作,所以我們要改寫grub

sudo nano /etc/default/grub

修改其中的一行就好,改成下面這樣

GRUBCMDLINELINUXDEFAULT="quiet splash nomodeset" 

儲存後好像要讓他重新讀取

sudo update-grub

如此一來每次就不用再按一次e編輯了,第一個問題解決

2. 解析度低,並且無法從cuda找到對應的GPU裝置:安裝對應的Driver驅動程式

儘管已經可以用ubuntu了,你會發現無法調整解析度,是因為ubuntu並沒有GTX1070的Driver,上網查的很多方法都無法,特別注意,不要想直接用apt-get來安裝,因為最新的驅動程式似乎無法搜尋到,你可能還會裝到比較舊的版本,造成許多問題,

是故我們到官網去下載run檔 http://www.nvidia.com.tw/Download/index.aspx?lang=tw 下載對應你GPU的版本,不要妄想用別的,最後會壞掉,網路上會推薦你用什麼bumblebee殺小的,至少我用1070他無法作用就是,乖乖下載

舉例來說GTX1070最後會下載到的檔案為:NVIDIA-Linux-x86_64-367.27.run

我們來安裝驅動程式,這裡有個很重要的地方,就是你無法直接在一般界面上安裝,按下ctrl+alt+F1可以切換到純命令界面,接著我們必須把GUI關掉

sudo service lightdm stop

接著cd到你放run file的地方

sudo chmod a+x NVIDIA-Linux-x8664-367.27.run
sudo ./NVIDIA-Linux-x8664-367.27.run

然後一直選yes,『但是!!!注意在最後有個X configuration的東西要選no!!!!』 continue直到安裝結束就好 接著你可以用sudo reboot重新開機,或是可以alt+右鍵一直按直到回到圖形界面,或是 sudo service lightdm restart也可以回到圖形界面,你就會發現解析度變好了,不然解析度差真的很痛苦。

2016/9/5更新
如何確定有安裝好?(一定要確認這點!!!)

nvidia-smi

2017/1/3更新
想要監控每個時間點的GPU的memory使用量可以用以下方法持續刷新

watch -n 0.5 nvidia-smi

由於之後有新的驅動程式367.44,但是他本身好像是有問題的,所以我之後重裝還是用367.27,版本也是很重要的,
如果你不小心裝了367.44,想要移除他,請使用以下指令

sudo ./NVIDIA-Linux-x8664-367.44.run --uninstall

安裝cuda時,記得不要一起安裝opengl(不過我後來只要安裝好nvidia driver沒有特別指定這個好像也沒什麼問題),不然可能會導致login loop,我最近一次成功的順序是先安裝cuda再安裝nvidia drivier(在另外一台電腦上直接安裝nvidia driver也是完全沒問題,不過記得X configuration那項一定要選no),安裝cuda要參考下面的連結

重要參考:
https://devtalk.nvidia.com/default/topic/878117/-solved-titan-x-for-cuda-7-5-login-loop-error-ubuntu-14-04-/
http://askubuntu.com/questions/762254/why-do-i-get-required-key-not-available-when-install-3rd-party-kernel-modules

如果你不小心裝了連自己都不知道是啥的版本,可以用下面方式刪除(通常網路上會建議你用ppa安裝,但是像367.44被安裝後是不能用的,這時候就需要下面這行)

sudo apt-get remove --purge nvidia-*

3. Cuda 7.5 or Cuda 8.0 ?

對GTX1070來說,千萬不要用7.5,直接用8.0,雖然網路很多人都說他們可以用,但是7.5我用了兩天都用不好,灰心下8.0用了兩三個小時就一切搞定了

(如果你用了7.5的話,你會發現跟driver超級不合,然後luarocks install cutorch的東西也會報錯)

2016/9/6更新 GTX780我用cuda 7.5是沒什麼問題的

https://developer.nvidia.com/cuda-toolkit 下載8.0並對應你所用的系統,記得下載run file好像才能選擇要否裝驅動,我們這裡是ubuntu14.04
我最後下載到的檔案名為:cuda_8.0.27_linux.run 可以使用以下命令安裝

sudo chmod a+x ./cuda_8.0.27_linux.run
sudo ./cuda_8.0.27_linux.run 

他會出現一堆字,就是合約要你看的,我們直接ctrl+C他會問你是否同意,就按照同意的概念去選之後的選項,
特別注意!!!!!!! 不要安裝他的driver因為我們前一步已經安裝過了,記得那項千萬要選NO

安裝好了後編輯一下來源

sudo nano ~/.bashrc

在最後加上後面這幾行就行了

export CUDA_HOME=/usr/local/cuda-8.0 
export LD_LIBRARY_PATH=${CUDA_HOME}/lib64 
 
PATH=${CUDA_HOME}/bin:${PATH} 
export PATH

可能再用一下source ~/.bashrc讓他讀一下什麼之類的

4. 安裝Cudnn

其實最重要的就是Cuda要選對,在GTX1070這個情況就是Cuda 8.0,Cudnn也是選對應8.0的即可

https://developer.nvidia.com/cudnn
去註冊並登入並下載對應你cuda的版本

(2017/07/24更新) 因為版本越來越新,很有可能會不小心下載到cudnn6,但是記得不要,用cudnn5就好,然後步驟要記得全部跑一遍。

我下載的是
cudnn-8.0-linux-x64-v5.0-ga.tgz
解壓縮後是一個名為cuda的資料夾,裡面有include和lib64兩個folder,我們需要把他們放到該放的地方去
假設我們把cuda資料夾放在Desktop上

cd Desktop/cuda/lib64
sudo cp ./* /usr/local/cuda/lib64/
cd
cd Desktop/cuda/include
sudo cp cudnn.h /usr/local/cuda/include/

更新他們的軟連結(注意其中的編號要依照你下載的版本而定)

cd /usr/local/cuda/lib64/ 
sudo rm -rf libcudnn.so libcudnn.so.5 
sudo ln -s libcudnn.so.5.0.5 libcudnn.so.5 
sudo ln -s libcudnn.so.5 libcudnn.so 

2016/9/9更新
有時候你安裝其他東西的時候會遇到說找不到lcufft這種感覺的東西,這時候也是軟連結的問題
像是在安裝spectral-lib的時候就會遇到這個問題
https://github.com/mbhenaff/spectral-lib
打入下面東西就沒事囉

sudo ln -s /usr/local/cuda/lib64/libcufft.so /usr/lib/libcufft.so
luarocks install spectralnet-scm-1.rockspec

5. 安裝Torch7

如果前面都完成了,你就可以放心安裝torch7了

# in a terminal, run the commands WITHOUT sudo
git clone https://github.com/torch/distro.git ~/torch --recursive
cd ~/torch; bash install-deps;
./install.sh

# 注意!千萬要執行下一行,不管安裝有沒有成功都一定要,不然你重開機會無法正常進入ubnutu (2017/07/04更新)
# On Linux with bash
source ~/.bashrc

# run luarocks WITHOUT sudo
$ luarocks install image
$ luarocks list

6. 其他問題

2017/02/23更新(許洸睿提供)
(1) Asus Prime Z270 主機板安裝在ubuntu 14.04 & 16.04 沒有網路:
https://ubuntuforums.org/showthread.php?t=2351572
下載 e1000e-3.3.5.3-patched.tar.gz 安裝
(2)替Linux 新增硬碟(磁碟分割、格式化與掛載)
https://blog.gtwang.org/linux/linux-add-format-mount-harddisk/

2017/07/04更新
(3)安裝後忘了使用source ~/.profile導致無法登入ubuntu
這個時候連GUI介面的terminal都無法進入,我此時是用teamviewer遠端,也無法進入非GUI介面,是故亂點teamviewer跳出來的連結進入chrome,再點chrome的下載紀錄,打開資料夾,隨便找個文件檔打開sublime,用open file的方式直接進入~/.profile進去編輯,把跟torch-activate的行拿掉,就可以開了。(汗
(4)安裝cutorch等失敗
由於torch是一直在更新的,如果你之後想要重新安裝全部的torch卻失敗,很可能是因為他們用了更新的cuda之類的,這時候就重新去官網下載新的cuda重新安裝就是了,舉例來說cuda_8.0.27和cuda_8.0.61是不一樣的,你很可能需要重新安裝。
(5)無論如何就是找不到libcudnn.so.5但是我明明就放進去了
http://blog.csdn.net/u014696921/article/details/60140264
必須感謝這個人,不然我可能也要一個星期......

sudo ldconfig /usr/local/cuda/lib64

似乎是cuda路徑設錯?不管如何這樣就可以了。

2018/05/16更新
ubuntu的login loop有很多種可能,這次遇到的是裝完nvidia driver後就會發生,而且不管裝哪個版本都會發生,後來才發現原來是螢幕的顯示hdmi插到主機板的插孔而不是GPU的插孔,插對後會沒畫面,重開機就完全正常了,真的非常該死。
DO NOT INSERT HDMI ON THE MOTHERBOARD!!! INSERT IT ON THE HOLE OF GPU!!!!!!