1. 程式人生 > >Caffe 實現多標籤分類

Caffe 實現多標籤分類

最近在用Caffe做驗證碼識別時,發現沒有用Tensorflow簡單(tensorflow中可以用one-hot, 參考我的另一篇blog:  http://blog.csdn.net/sushiqian/article/details/78305340 ),需要修改Caffe的原始碼,做完後,覺得也不復雜。

1. 首先,就是修改原始碼了

    本文介紹的方法是修改ImageDataLayer,修改下面的三個檔案:

          $CAFFE_ROOT/src/caffe/proto/caffe.proto

          $CAFFE_ROOT/include/caffe/layers/image_data_layer.hpp

          $CAFFE_ROOT/src/caffe/layers/image_data_layer.cpp

          (1) 修改 caffe.proto

                在檔案 caffe.proto 裡的 message ImageDataParameter {  } 裡新增新的一項,用於表示標籤的維度

    optional int32 label_dim = 13 [default = 1];

  

  (2) 修改 image_data_layer.hpp

   把原先成員  lines_  的定義修改為

   vector<std::pair<std::string, vector<int> > > lines_;


  (3) 修改 image_data_layer.cpp

          修改函式 DataLayerSetUp()

          新的程式碼如下:

  int label_dim = this->layer_param_.image_data_param().label_dim();
  string filename;
  while (infile >> filename){
     vector<int> label(label_dim, 0);
     for(int i = 0; i < label_dim; i++) {
        infile >> label[i];        
     }
     lines_.push_back(std::make_pair(filename, label));
  }
vector<int> label_shape(2);
label_shape[0] = batch_size;
label_shape[1] = label_dim;


  修改函式 load_batch()

int label_dim = image_data_param.label_dim();
for(int i = 0;i < label_dim;++i){    
    prefetch_label[item_id * label_dim + i] = lines_[lines_id_].second[i];
}


   (4) 用 make 編譯

  $CAFFE_ROOT/build$ make

        $CAFFE_ROOT/build$ make install

2. 編寫用於訓練的網路結構

 可以在caffe examples 裡的某個net上修改一個, 例如,本文中所講例子是在mnist中LeNet(lenet_train_test.prototxt)基礎上修改出一個net

     captcha_train_test.prototxt

name: "captcha"
layer {  
  name: "Input"  
  type: "ImageData"  
  top: "data"  
  top: "label"  
  include {  
    phase: TRAIN  
  }  
  transform_param {  
    scale: 0.00390625
  }  
  image_data_param {  
    source: "/home/mark/data/train.txt"  
    root_folder: "/home/mark/data/"  
    new_height: 60  
    new_width: 160  
    is_color: true  
    batch_size: 50  
    shuffle: true
    label_dim: 4
  }  
}  

layer {  
  name: "Input"  
  type: "ImageData"  
  top: "data"  
  top: "label"  
  include {  
    phase: TEST  
  }  
  transform_param {  
    scale: 0.00390625
  }  
  image_data_param {  
    source: "/home/mark/data/test.txt"  
    root_folder: "/home/mark/data/"  
    new_height: 60  
    new_width: 160  
    is_color: true  
    batch_size: 20  
    shuffle: true
    label_dim: 4
  }  
}  

layer {
  name: "slice"
  type: "Slice"
  bottom: "label"
  top: "label_1"
  top: "label_2"
  top: "label_3"
  top: "label_4"
  slice_param {
    axis: 1
    slice_point:1
    slice_point:2
    slice_point:3
  }
} 

layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}

layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 100
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_1"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_2"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_3"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_4"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_4"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "accuracy1"
  type: "Accuracy"
  bottom: "ip3_1"
  bottom: "label_1"
  top: "accuracy1"
  include {
    phase: TEST
  }
}
layer {
  name: "loss1"
  type: "SoftmaxWithLoss"
  bottom: "ip3_1"
  bottom: "label_1"
  top: "loss1"
}

layer {
  name: "accuracy2"
  type: "Accuracy"
  bottom: "ip3_2"
  bottom: "label_2"
  top: "accuracy2"
  include {
    phase: TEST
  }
}
layer {
  name: "loss2"
  type: "SoftmaxWithLoss"
  bottom: "ip3_2"
  bottom: "label_2"
  top: "loss2"
}

layer {
  name: "accuracy3"
  type: "Accuracy"
  bottom: "ip3_3"
  bottom: "label_3"
  top: "accuracy3"
  include {
    phase: TEST
  }
}
layer {
  name: "loss3"
  type: "SoftmaxWithLoss"
  bottom: "ip3_3"
  bottom: "label_3"
  top: "loss3"
}

layer {
  name: "accuracy4"
  type: "Accuracy"
  bottom: "ip3_4"
  bottom: "label_4"
  top: "accuracy4"
  include {
    phase: TEST
  }
}
layer {
  name: "loss4"
  type: "SoftmaxWithLoss"
  bottom: "ip3_4"
  bottom: "label_4"
  top: "loss4"
}

/home/mark/data/train.txt 裡面存放的是訓練所用圖片的路徑和標籤,

該網路的結構圖如下所示:

solver 檔案

captcha_solver.prototxt

# The train/test net protocol buffer definition
net: "examples/captcha/captcha_train_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# covering the full 9,800 testing images.
test_iter: 200
# Carry out testing every 200 training iterations.
test_interval: 200
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.001
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "examples/captcha/captcha"
# solver mode: CPU or GPU
solver_mode: GPU
在CAFFE_ROOT 下執行下面的命令開始訓練
./build/tools/caffe train --solver=examples/captcha/captcha_solver.prototxt


Note: 訓練過程中如果發現 loss 不下降,可以把學習率(base_lr)調小一點試試

3. 模型訓練成功後,進行測試

 首先,需要一個deploy.prototxt檔案,在captcha_train_test.prototxt的基礎上修改,修改後儲存為 captcha_deploy.prototxt  內容如下

name: "captcha"

input: "data"  
input_dim: 1   # batchsize  
input_dim: 3   # number of channels - rgb  
input_dim: 60  # height
input_dim: 160 # width

layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}

layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 100
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_1"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_2"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_3"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_3"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "ip3_4"
  type: "InnerProduct"
  bottom: "ip2"
  top: "ip3_4"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

layer {
  name: "prob1"
  type: "Softmax"
  bottom: "ip3_1"
  top: "prob1"
}
layer {
  name: "prob2"
  type: "Softmax"
  bottom: "ip3_2"
  top: "prob2"
}
layer {
  name: "prob3"
  type: "Softmax"
  bottom: "ip3_3"
  top: "prob3"
}
layer {
  name: "prob4"
  type: "Softmax"
  bottom: "ip3_4"
  top: "prob4"
}

編寫測試程式碼:
import numpy as np  
import os  
import sys 
os.environ['GLOG_minloglevel'] = '3' 
import caffe  
  
CAFFE_ROOT = '/home/mark/caffe'  
deploy_file_name = 'captcha_deploy.prototxt'  
model_file_name  = 'captcha_iter_10000.caffemodel' 

IMAGE_HEIGHT = 60
IMAGE_WIDTH = 160
IMAGE_CHANNEL = 3

def classify(imageFileName):
    deploy_file = CAFFE_ROOT + '/examples/captcha/' + deploy_file_name  
    model_file  = CAFFE_ROOT + '/examples/captcha/' + model_file_name
    #初始化caffe 
    net = caffe.Net(deploy_file, model_file, caffe.TEST)

    #資料預處理    
    transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})    
    transformer.set_transpose('data', (2, 0, 1))#pycaffe讀取的圖片檔案格式為H×W×C,需轉化為C×H×W

    #pycaffe將圖片儲存為[0, 1], 如果模型輸入用的是0~255的原始格式,需要做如下轉換
    #transformer.set_raw_scale('data', 255)   

    transformer.set_channel_swap('data', (2, 1, 0))#caffe中圖片是BGR格式,而原始格式是RGB,所以要轉化 

    # 將輸入圖片格式轉化為合適格式(與deploy檔案相同)
    net.blobs['data'].reshape(1, IMAGE_CHANNEL, IMAGE_HEIGHT, IMAGE_WIDTH)

    #讀取圖片
    #引數color: True(default)是彩色圖,False是灰度圖
    img = caffe.io.load_image(imageFileName, color=True)

    #資料輸入、預處理
    net.blobs['data'].data[...] = transformer.preprocess('data', img)

    #前向迭代,即分類
    out = net.forward()

    #求出每個標籤概率最大值的下標
    result = []
    predict1 = out['prob1'][0].argmax()
    result.append(predict1)

    predict2 = out['prob2'][0].argmax()
    result.append(predict2)

    predict3 = out['prob3'][0].argmax()
    result.append(predict3)

    predict4 = out['prob4'][0].argmax()
    result.append(predict4)    
    
    return result
        
if __name__ == '__main__':

   imgList = sys.argv[1:]
   for captcha in imgList:
       predict = classify(captcha)
       print "captcha:", captcha, "  predict:", predict

   

執行上面的指令碼,進行測試

測試用到的圖片如下


相關推薦

caffe實現標籤分類

最近在參加一個識別的競賽,專案裡涉及了許多類別的分類,原本打算一個大的類別訓練一個分類模型,但是這樣會比較麻煩,對於同一圖片的分類會重複計算分類網路中的卷積層,浪費計算時間和效率。後來發現現在深度學習中的多工學習可以實現多標籤分類,所有的類別只需要訓練一個分類模型就行,其不同屬性的類別之間是共享卷積層的。我

Caffe 實現標籤分類 支援Multi-Label的LMDB資料格式輸入

Caffe自帶的影象轉LMDB介面只支援單label,對於多label的任務,可以使用HDF5的格式,也可以通過修改caffe程式碼來實現, 我的文章Caffe 實現多標籤分類 裡介紹了怎麼通過修改ImageDataLayer來實現Multilabel的任務, 本篇文章介紹

Caffe 實現標籤分類

最近在用Caffe做驗證碼識別時,發現沒有用Tensorflow簡單(tensorflow中可以用one-hot, 參考我的另一篇blog:  http://blog.csdn.net/sushiqian/article/details/78305340 ),需要修改Caf

Caffe實現標籤影象分類(1)——基於Python介面實現標籤影象分類(VOC2012)

1.前言         Caffe可以通過LMDB或LevelDB資料格式實現影象資料及標籤的輸入,不過這隻限於單標籤影象資料的輸入。由於研究生期間所從事的研究是影象標註領域,在進行影象標註時,每幅影象都是多標籤的,因此在使用Caffe進行遷移學習時需要實現多標籤影象資料

基於keras實現標籤分類(multi-label classification)

首先討論多標籤分類資料集(以及如何快速構建自己的資料集)。 之後簡要討論SmallerVGGNet,我們將實現的Keras神經網路架構,並用於多標籤分類。 然後我們將實施SmallerVGGNet並使用我們的多標籤分類資料集對其進行訓練。 最後,我們將通過在示例影象上測試我

2.CNN圖片標籤分類(基於TensorFlow實現驗證碼識別OCR)

上一篇實現了圖片CNN單標籤分類(貓狗圖片分類任務) 地址:juejin.im/post/5c0739… 預告:下一篇用LSTM+CTC實現不定長文字的OCR,本質上是一種不固定標籤個數的多標籤分類問題 本文所用到的10w驗證碼資料集百度網盤下載地址(也可使用下文程式碼自行生成): pan.baidu

Caffe中LMDB介面實現標籤資料準備及訓練

有不少部落格講Caffe多標籤輸入的問題,但總覺得講的不夠透徹,在實踐角度上沒有給出詳細的指導,所以本文力求能給出詳細的實踐過程和說明。 Caffe多標籤輸入常用的的方法有以下幾種: 1. 修改Caffe原始碼使其支援多標籤輸入,參考CSDN部落格《

caffe工學習之標籤分類

最近在參加一個識別的競賽,專案裡涉及了許多類別的分類,原本打算一個大的類別訓練一個分類模型,但是這樣會比較麻煩,對於同一圖片的分類會重複計算分類網路中的卷積層,浪費計算時間和效率。後來發現現在深度學習中的多工學習可以實現

caffe實現任務學習

mdb ice 學習 caf tro con targe slice eset 1. 采用多label的lmdb+Slice Layer的方法 http://blog.csdn.net/u013010889/article/details/53098346 2. 修改數

標籤分類的結果評估---macro-average和micro-average介紹

一,多分類的混淆矩陣 多分類混淆矩陣是二分類混淆矩陣的擴充套件 祭出程式碼,畫線的那兩行就是關鍵啦: 二,檢視多分類的評估報告 祭出程式碼,使用了classicfication_report() 三,巨集平均與微平均 公式是神看的,我是學弱...直接看例子,沒有複雜的公

標籤分類(multi-label classification)

意義         網路新聞往往含有豐富的語義,一篇文章既可以屬於“經濟”也可以屬於“文化”。給網路新聞打多標籤可以更好地反應文章的真實意義,方便日後的分類和使用。 難點 (1)類標數量不確定,有些樣本可能只有一個類標,有些樣本的類標可能高達幾十甚至上百個。 

針對科技文章的標籤分類

0. 起   最近沒有更新。暑假之後學的東西也比較雜,看了一下基於DL的智慧美顏,感覺入了個大坑。前前後後看了傳統的輪廓提取演算法和FCN等等,調程式碼巴拉巴拉幾個星期就這麼過了。前幾天看ACM的best paper也覺得很有意思,兩個步驟:1)給圖片打標題,屬於多媒體計算和NLP的結合 2)由標題生成詩歌

資料庫百科---實現標籤篩選

一、背景 在一些視訊網站(如:優酷、愛奇藝等),都會有篩選功能。 但當我使用這個篩選功能時,發現其只能進行單型別篩選,例如電影型別只能按某一種型別進行篩選。如果想看既是科幻電影又是戰爭的電影呢?顯然單個標籤型別篩選是滿足不了這個需求的,接下來簡要談談我的實現方法。

解決標籤分類問題(包括案例研究)

由於某些原因,迴歸和分類問題總會引起機器學習領域的大部分關注。多標籤分類在資料科學中是一個比較令人頭疼的問題。在這篇文章中,我將給你一個直觀的解釋,說明什麼是多標籤分類,以及如何解決這個問題。 1.多標籤分類是什麼? 讓我們來看看下面的圖片。 如果我問你這幅圖中有一

來自語義資訊理論的標籤分類方法——有簡單方法幹嘛要用複雜的?

流行的多標籤分類方法大多把多分類化為多個二分類。流行的有One-to-Rest方法和Binary Relevance方法。 One-to-Rest方法的問題是, 圍繞一個標籤y1分類, 把一個沒有y的例子當成y1的反例, 這時不對的。 比如兩個例子(25歲, “年輕人”)和

Python和PyTorch對比實現標籤softmax + cross-entropy交叉熵損失及反向傳播

相關 關於 softmax + cross-entropy 的詳細介紹, 請參考 : BrightLamp. 多標籤softmax + cross-entropy交叉熵損失函式詳解及反向傳播中的梯度求導[EB/OL]. https://blog.csdn.net

標籤分類之改進的神經網路方法

Improved Multilabel Classification with Neural Networks 2008 International Conference on Parallel Problem Solving from Nature Rafa

keras 自定義ImageDataGenerator用於標籤分類

感想 keras提供了flow_from_directory用於單個標籤分類,但是對圖片的多標籤分類沒有支援,這就需要我們自己動手實現ImageDataGenerator,我這裡把我實現的用於多標籤分類的自定義DataGenerator分享出來,讀者可以根據自己的情況來進行修改。 資料集

標籤分類(multilabel classification )

為了區分多分類,先從基本的概念上開始: 二分類:投擲硬幣,結果是正面或者反面,可以選擇的類別只有兩個,並且確切的結果只能有一個,類別互斥,即一枚硬幣的投擲結果要麼是正面要麼是反面,不同同時出現 多分

keras解決標籤分類問題

multi-class classification problem: 多分類問題是相對於二分類問題(典型的0-1分類)來說的,意思是類別總數超過兩個的分類問題,比如手寫數字識別mnist的label總數有10個,每一個樣本的標籤在這10箇中取一個。 mult