1. 程式人生 > >【用Python學習Caffe】4. 設計自己的網路結構

【用Python學習Caffe】4. 設計自己的網路結構

4. 設計自己的網路結構

通過前文的例子,我們都知道了Caffe的網路都是一個prototxt的網路結構配置檔案定義的,該檔案可以用文字工具開啟,開啟後,我們可以看到如下結構:

    layer {
      name: "data"
      type: "Data"
      top: "data"
      top: "label"
      transform_param {
        scale: 0.00390625
      }
      data_param {
        source: "../../data/mnist/mnist_train_lmdb"
        batch_size: 64
        backend: LMDB
      }
    }
    layer {
      name: "conv1"
      type: "Convolution"
      bottom: "data"
      top: "conv1"
      param {
        lr_mult: 1
        decay_mult: 1
      }
      param {
        lr_mult: 2
        decay_mult: 0
      }
      convolution_param {
        num_output: 20
        pad: 0
        kernel_size: 5
        stride: 1
        weight_filler {
          type: "xavier"
        }
        bias_filler {
          type: "constant"
          value: 0
        }
      }
    }
    layer {
      name: "pool1"
      type: "Pooling"
      bottom: "conv1"
      top: "pool1"
      pooling_param {
        pool: MAX
        kernel_size: 2
        stride: 2
      }
    }
    ...

在prototxt檔案中,層都是用layer{}的結構表示,而裡面包含的層的引數可以在caffe.proto檔案中找到,比如說Data型別的結構由message DataParameter所定義,Convolution型別的結構由message ConvolutionParameter所定義。
具體說明下:

  • name表示該層的名稱
  • type表示該層的型別,如Data或者Convolution型別
  • top表示該層後接的Blob結構的名稱
  • bottom表示該層前接的Blob資料結構的名稱
  • *_param表示該層的引數,比如對於某卷積層conv1來說,convolution_param
    • num_output
      表示輸出通道數
    • pad表示卷積pad
    • kernel_size表示卷積核的大小

對於如何使用層結構,一方面我們可以從model_libs.pycaffenet.py等檔案找到如何使用層結構的例子。另外可能參考這個教程,裡面給出caffe的所有層的Python函式使用例子。
另一方面我們可以參照caffe的C++類定義。在我們知道各層的描述後,可以將這些層結構拼接成我們自己的網路,手動在文字工具中直接編寫prototxt檔案。

但對於一些非常複雜的網路,很多情況下可能有成百上千的層。這樣的情況下,我們能難人工一層接一層的編寫prototxt檔案,而且還很容易出錯,因此可以通過python來直接自動生成結構檔案。

在caffe目錄下的example/pycaffe資料夾的caffenet.py給出瞭如何直接生成一個多層線性分類器的網路配置檔案prototxt檔案,而本節還將介紹一個直接生成mnist LeNet5網路的訓練、測試及實施的三個網路配置檔案prototxt。而我們將以後章節中介紹通過三個檔案完成LeNet5網路的訓練及測試任務。

Tips:可以將一些常用的網路結構打包成基礎體,比如在model_lib.py檔案中將VGG網路、AlexNet等常用網路打包成基礎網路。另外訓練、測試及實施的三個網路的中間結構是一致的,只是輸入層和最終輸出層是不一樣的,因此將可以中間相同層都用一個固定函式生成。

生成網路配置主要分為如下幾個部分:

4.1 配置基礎網路結構

這裡生成訓練、測試及實施的三個網路中都相同的網路中間部件

    # 此函式生成LeNet5的主體結構
    def lenet5_body(net, from_layer):
        # 網路引數
        kwargs = {
            # param定義學習率,這裡是指基礎學習率step的情況,lt_mult乘以基礎學習率為實際學習率,為0表示權重不更新,decay_mult同權重衰減相關
            'param': [dict(lr_mult=1, decay_mult=1), dict(lr_mult=2, decay_mult=0)],
            'weight_filler': dict(type='xavier'), # 權重初始化模式
            'bias_filler': dict(type='constant', value=0)} # 權重偏差初始化模式

        # 判斷是否存在from_layer層
        assert from_layer in net.keys()
        # conv1
        net.conv1 = L.Convolution(net[from_layer], kernel_size=5, stride=1, num_output=20, pad=0, **kwargs)
        net.pool1 = L.Pooling(net.conv1, pool=P.Pooling.MAX, kernel_size=2, stride=2)
        net.conv2 = L.Convolution(net.pool1, kernel_size=5, stride=1, num_output=50, pad=0, **kwargs)
        net.pool2 = L.Pooling(net.conv2, pool=P.Pooling.MAX, kernel_size=2, stride=2)
        net.ip1 = L.InnerProduct(net.pool2, num_output=500, **kwargs)
        net.relu1 = L.ReLU(net.ip1, in_place=True)
        net.ip2 = L.InnerProduct(net.relu1, name='ip2', num_output=10, **kwargs)

4.2 構建整體網路

訓練、測試及實施的三個網路的結構有些許不同,主要集中在輸入層和輸出層。

    # 訓練網路
    train_net = caffe.NetSpec()  # 基礎網路
    # 帶標籤的資料輸入層
    train_net.data, train_net.label = L.Data(source=train_data,backend=P.Data.LMDB, batch_size=64,ntop=2,transform_param=dict(scale=0.00390625))
    # 生成LeNet5的主體結構
    lenet5_body(train_net, 'data')
    # 生成誤差損失層
    train_net.loss = L.SoftmaxWithLoss(train_net.ip2, train_net.label)
    # 測試網路
    test_net = caffe.NetSpec()  # 基礎網路
    # 帶標籤的資料輸入層
    test_net.data, test_net.label = L.Data(source=test_data, batch_size=100, backend=P.Data.LMDB, ntop=2,transform_param=dict(scale=0.00390625))
    # 生成LeNet5的主體結構
    lenet5_body(test_net, 'data')
    # 生成誤差損失層
    test_net.loss = L.SoftmaxWithLoss(test_net.ip2, test_net.label)
    # 新增一個精確層
    test_net.accuracy = L.Accuracy(test_net.ip2, test_net.label)
    # 實施網路
    deploy_net = caffe.NetSpec()  # 基礎網路
    # 帶標籤的資料輸入層
    deploy_net.data = L.Input(input_param=dict(shape=dict(dim=[64,1,28,28])))
    # 生成LeNet5的主體結構
    lenet5_body(deploy_net, 'data')
    deploy_net.prob = L.Softmax(deploy_net.ip2)

4.3 prototxt檔案的儲存

通過to_proto將網路儲存成prototxt檔案

    # 儲存訓練檔案
    with open(model_root+'train.prototxt', 'w') as f:
        print('name: "LenNet5_train"', file=f)
        print(train_net.to_proto(), file=f)
    with open(model_root+'test.prototxt', 'w') as f:
        print('name: "LenNet5_test"', file=f)
        print(test_net.to_proto(), file=f)
    with open(model_root+'deploy.prototxt', 'w') as f:
        print('name: "LenNet5_test"', file=f)
        print(deploy_net.to_proto(), file=f)

4.4 具體程式碼下載