天天看點

MXNet 核心接口ContextSymbolMetric

from mxnet import cpu, gpu, nd           

Context

Context

是模型的資料放置的環境:

a = nd.array([2, 4], ctx=cpu())
a1 = nd.array([3, 7], ctx=gpu(0))
a.context, a1.context           
(cpu(0), gpu(0))
           

當然,也可以在 CPU 與 GPU 之間進行複制。

a2 = a.copyto(a1)   # 要求 a 與 a1 有相同的 shape
a1 is a2, a2.context           
(True, gpu(0))
           
a1  # a1 被修改了           
[2. 4.]
<NDArray 2 @gpu(0)>
           

為了進行深度複制,需要使用:

a3 = nd.array([3, 9])
a4 = a3.as_in_context(gpu(0))
a3 is a4, a3.context, a4.context           
(False, cpu(0), gpu(0))
           

Symbol

  • Symbol

    的基本函數 - 定義計算圖
  • Symbol.infer_type

    : 推導目前 Symbol 所依賴的所有 Symbol 資料類型
  • Symbol.infer_shape

    : 推導目前 Symbol 所依賴的所有 Symbol 的形狀
  • Symbol.list_argments

    : 列出目前 Symbol 所用到的基本參數名稱
  • Symbo.list_outputs

    : 列出目前 Symbol 的輸出名稱
  • Symbol.list_auxiliary_states

    : 列出目前 Symbol 的輔助參量名稱
from mxnet import sym, symbol           
X = sym.Variable('X')
out = sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
out = symbol.BatchNorm(out, name='batchnorm')
out = sym.Activation(data=out, act_type='relu')
out = sym.FullyConnected(data=out, name='fc2', num_hidden=10)
arg_types, out_types, aux_types = out.infer_type(X='float32')
arg_types, out_types, aux_types           
([numpy.float32,
  numpy.float32,
  numpy.float32,
  numpy.float32,
  numpy.float32,
  numpy.float32,
  numpy.float32],
 [numpy.float32],
 [numpy.float32, numpy.float32])
           
arg_shapes, out_shapes, aux_shapes = out.infer_shape(X=(100,784))
arg_shapes, out_shapes, aux_shapes           
([(100, 784), (1000, 784), (1000,), (1000,), (1000,), (10, 1000), (10,)],
 [(100, 10)],
 [(1000,), (1000,)])
           
out.list_arguments()           
['X',
 'fc1_weight',
 'fc1_bias',
 'batchnorm_gamma',
 'batchnorm_beta',
 'fc2_weight',
 'fc2_bias']
           
out.list_outputs()           
['fc2_output']
           
out.list_auxiliary_states()           
['batchnorm_moving_mean', 'batchnorm_moving_var']
           

Symbol 如何擷取中間節點

在定義好一個網絡之後,如何去擷取任何一個節點的輸出值對于深度神經網絡的遷移來說非常重要,因為在使用時通常并不是自己從頭開始訓練一個網絡,而是在别人訓練好的網絡基礎上根據自己的問題進行微調。

X = sym.Variable('X')
fc1 = sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
act = sym.Activation(data=fc1, act_type='relu')
fc2 = sym.FullyConnected(data=act, name='fc2', num_hidden=10)
net = sym.SoftmaxOutput(fc2, name="softmax")
net.get_internals()           
<Symbol group [X, fc1_weight, fc1_bias, fc1, activation0, fc2_weight, fc2_bias, fc2, softmax_label, softmax]>
           
subnet = net.get_internals()['fc2_output']
subnet, subnet.list_arguments()           
(<Symbol fc2>, ['X', 'fc1_weight', 'fc1_bias', 'fc2_weight', 'fc2_bias'])
           

首先使用

get_internals()

擷取整個 Symbol 的子圖,輸出是整個内部節點的輸出節點清單。然後可以通過索引擷取網絡的子圖。在上面的例子中,我們擷取網絡層的倒數第二層 fc2,可以看到 fc2 也是一個 Symbol 對象。

fc2.list_arguments()           
['X', 'fc1_weight', 'fc1_bias', 'fc2_weight', 'fc2_bias']
           

同理,有:

subnet = net.get_internals()['fc1_output']
subnet.list_arguments()           
['X', 'fc1_weight', 'fc1_bias']
           

圖的拼接

假如,我們拿到了别人訓練好的網絡參數檔案和網絡結構檔案,我們可以固定網絡圖的前部分,在尾部添加額外的 Symbol 節點,但是在網絡的頭部替換輸入節點較困難。

X = sym.Variable('X')
fc1 =  sym.FullyConnected(data=X, name='fc1', num_hidden=1000)
act =  sym.Activation(data=fc1, act_type='relu',name='act')
fc2 =  sym.FullyConnected(data=act, name='fc2', num_hidden=10)
net = sym.SoftmaxOutput(fc2,name="softmax")
net.save('model.symbol.json')           

如上面所示,我們先定義好了一個 包含 10 個輸出節點網絡,然後将 Smbol 網絡儲存到 json 檔案。接下來我們重新加載這個 json 檔案,并且在網絡結尾處重新修改網絡層的輸出為 20。

net = sym.load('model.symbol.json')
net.get_internals().list_outputs()           
['X',
 'fc1_weight',
 'fc1_bias',
 'fc1_output',
 'act_output',
 'fc2_weight',
 'fc2_bias',
 'fc2_output',
 'softmax_label',
 'softmax_output']
           
newnet = net.get_internals()['act_output']
newnet = sym.FullyConnected(data=fc1,num_hidden=20,name ='fc2_new')
newnet = sym.SoftmaxOutput(data=newnet,name='softmax_new')
newnet.get_internals().list_outputs()           
['X',
 'fc1_weight',
 'fc1_bias',
 'fc1_output',
 'fc2_new_weight',
 'fc2_new_bias',
 'fc2_new_output',
 'softmax_new_label',
 'softmax_new_output']
           

Metric

Metric 是用來衡量模型效果的接口

當我們定義好一個 Metric,比如說 Accuracy , 然後将 Accuracy 交給 Module 托管的時候,在每個 Epoch 結束時,會自動調用 update 方法,計算 正确預測的樣本數量和總共的樣本數量,進而調用父類中的 get 方法,計算出最後的 Acc。

from mxnet import metric

predicts = [nd.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
labels   = [nd.array([0, 1, 1])]
acc = metric.Accuracy()
acc.update(preds = predicts, labels = labels)
acc.get()           
('accuracy', 0.6666666666666666)
           

Metric Hack 分析

如果我們想要定義 自己的 Metric 類,需要完成下面幾步:

  • 繼承

    metric.EvalMetric

    接口,重新定義

    update

    方法,update 傳入參數分析:
    • labels :

      list

      類型,每個元素對應 DataBatch 中的

      label

    • predicts

      :

      list

      類型, 是 Loss Symbol 中 label 外的輸入,是以

      list

      中的元素個數與網絡上

      loss

      的個數有關
  1. 函數需要完成:
  • 更新屬性

    sum_metric

    num_inst

    的值,mxnet 會調用

    get

    函數中的

    self.sum_metric / self.num_inst

    來計算目前

    metric

    的輸出值。
  • 與一個特殊的

    Callback

    類有關:

    Speedometer

    會自動列印出所有

    metric

    的值。

轉載:

https://www.imooc.com/article/278837