1. 程式人生 > >學習筆記:caffe2 教程記錄六

學習筆記:caffe2 教程記錄六

接著caffe2 教程記錄五,這個是第六篇

##7.建立自己的資料集

如何建立自己的資料集?
因此Caffe2使用二進位制DB格式來儲存我們想要訓練模型的資料。 Caffe2 DB是鍵值儲存的美化名稱,其中鍵通常是隨機的,因此批次大約是i.i.d,但是這些值是真實的:它們包含您希望訓練演算法攝取的特定資料格式的序列化字串。因此,儲存的資料庫看起來(語義上)像這樣:
key1 value1 key2 value2 key3 value3 ...
對於DB,它將鍵和值視為字串,但您可能需要結構化內容。一種方法是使用TensorProtos協議緩衝區:它基本上包含張量(也稱為多維陣列)以及張量資料型別和形狀資訊。然後,可以使用TensorProtosDBInput運算子將資料載入到SGD訓練方式中。
在這裡,我們將向您展示如何建立自己的資料集的一個示例。為此,我們將使用UCI Iris資料集 - 這是一個非常流行的經典資料集,用於分類虹膜花。它包含4個代表花朵尺寸的實值特徵,並將東西分為3種類型的虹膜花。資料集可以在這裡(

here)下載 。

# First let's import some necessities
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

%matplotlib inline
import urllib2 # for downloading the dataset from the web.
import numpy as np
from matplotlib import pyplot
from StringIO import StringIO
from caffe2.python import core, utils, workspace
from caffe2.proto import caffe2_pb2
f = urllib2.urlopen('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data')
raw_data = f.read()
print('Raw data looks like this:')
print(raw_data[:100] + '...')

輸出的結果: 

 


# load the features to a feature matrix.# load  
features = np.loadtxt(StringIO(raw_data), dtype=np.float32, delimiter=',', usecols=(0, 1, 2, 3))
# load the labels to a feature matrix
label_converter = lambda s : {'Iris-setosa':0, 'Iris-versicolor':1, 'Iris-virginica':2}[s]
labels = np.loadtxt(StringIO(raw_data), dtype=np.int, delimiter=',', usecols=(4,), converters={4: label_converter})

在我們進行培訓之前,通常有益的一件事是將資料集分成培訓和測試。 在這種情況下,讓我們隨機抽取資料,使用前100個數據點進行訓練,剩下的50個進行測試。 對於更復雜的方法,您可以使用例如 交叉驗證將您的資料集分成多個培訓和測試拆分。 閱讀更多關於交叉驗證的資訊。 

random_index = np.random.permutation(150)
features = features[random_index]
labels = labels[random_index]

train_features = features[:100]
train_labels = labels[:100]
test_features = features[100:]
test_labels = labels[100:]
# Let's plot the first two features together with the label.
# Remember, while we are plotting the testing feature distribution
# here too, you might not be supposed to do so in real research,
# because one should not peek into the testing data.
legend = ['rx', 'b+', 'go']
pyplot.title("Training data distribution, feature 0 and 1")
for i in range(3):
    pyplot.plot(train_features[train_labels==i, 0], train_features[train_labels==i, 1], legend[i])
pyplot.figure()
pyplot.title("Testing data distribution, feature 0 and 1")
for i in range(3):
    pyplot.plot(test_features[test_labels==i, 0], test_features[test_labels==i, 1], legend[i])

輸出結果:

 

現在,正如所承諾的那樣,讓我們把東西放到Caffe2資料庫中。 在這個DB中,會發生的是我們將使用“train_xxx”作為鍵,並使用TensorProtos物件為每個資料點儲存兩個張量:一個作為特徵,一個作為標籤。 我們將使用Caffe2的Python DB介面來實現。

# First, let's see how one can construct a TensorProtos protocol buffer from numpy arrays.
feature_and_label = caffe2_pb2.TensorProtos()
feature_and_label.protos.extend([
    utils.NumpyArrayToCaffe2Tensor(features[0]),
    utils.NumpyArrayToCaffe2Tensor(labels[0])])
print('This is what the tensor proto looks like for a feature and its label:')
print(str(feature_and_label))
print('This is the compact string that gets written into the db:')
print(feature_and_label.SerializeToString())

 輸出結果:

# Now, actually write the db.

def write_db(db_type, db_name, features, labels):
    db = core.C.create_db(db_type, db_name, core.C.Mode.write)
    transaction = db.new_transaction()
    for i in range(features.shape[0]):
        feature_and_label = caffe2_pb2.TensorProtos()
        feature_and_label.protos.extend([
            utils.NumpyArrayToCaffe2Tensor(features[i]),
            utils.NumpyArrayToCaffe2Tensor(labels[i])])
        transaction.put(
            'train_%03d'.format(i),
            feature_and_label.SerializeToString())
    # Close the transaction, and then close the db.
    del transaction
    del db

write_db("minidb", "iris_train.minidb", train_features, train_labels)
write_db("minidb", "iris_test.minidb", test_features, test_labels)

現在,讓我們建立一個非常簡單的網路,它只包含一個TensorProtosDBInput運算子,以展示我們如何從我們建立的資料庫載入資料。 對於培訓,您可能希望執行更復雜的操作:建立網路,訓練網路,獲取模型以及執行預測服務。 為此,您可以檢視MNIST教程以獲取詳細資訊。

net_proto = core.Net("example_reader")
dbreader = net_proto.CreateDB([], "dbreader", db="iris_train.minidb", db_type="minidb")
net_proto.TensorProtosDBInput([dbreader], ["X", "Y"], batch_size=16)

print("The net looks like this:")
print(str(net_proto.Proto()))

輸出結果:

workspace.CreateNet(net_proto)

輸出:true

# Let's run it to get batches of features.
workspace.RunNet(net_proto.Proto().name)
print("The first batch of feature is:")
print(workspace.FetchBlob("X"))
print("The first batch of label is:")
print(workspace.FetchBlob("Y"))

# Let's run again.
workspace.RunNet(net_proto.Proto().name)
print("The second batch of feature is:")
print(workspace.FetchBlob("X"))
print("The second batch of label is:")
print(workspace.FetchBlob("Y"))

輸出的結果:

##8.MNIST - 手寫識別

在本教程中,我們將向您展示如何訓練小型卷積神經網路(CNN)。 我們將在MNIST資料集上訓練模型,該資料集由標記的手寫數字組成。 來自資料集的每個樣本是單個手寫數字的28x28畫素灰度(1通道)影象,標籤是0到9的整數。
對於我們的模型,我們將構建LeNet模型,其中Sigmoid啟用被ReLU取代。 本教程還將提供使用簡單的多層感知器(MLP)模型的選項,該模型不包括任何卷積運算子。 您可以通過下面的USE_LENET_MODEL標誌選擇所需的模型。
在開始之前,請注意我們將在本教程中使用ModelHelper類。 這個類可以幫助我們自然地處理引數初始化,並使我們不必分別維護param_init_net和net物件。
在我們開始之前,讓我們匯入一些必須的依賴。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
%matplotlib inline
from matplotlib import pyplot
import numpy as np
import os
import shutil
import operator
import caffe2.python.predictor.predictor_exporter as pe

from caffe2.python import (
    brew,
    core,
    model_helper,
    net_drawer,
    optimizer,
    visualize,
    workspace,
)

# If you would like to see some really detailed initializations,
# you can change --caffe2_log_level=0 to --caffe2_log_level=-1
core.GlobalInit(['caffe2', '--caffe2_log_level=0'])
print("Necessities imported!")

# If True, use the LeNet CNN model
# If False, a multilayer perceptron model is used
USE_LENET_MODEL = True

資料下載
我們將處理本教程所需的資料下載,並通過在使用者主目錄中設定名為caffe2_notebooks的頂級本地資料夾來跟蹤培訓期間的統計資訊。 對於資料下載,我們需要設定tutorial_data資料夾,對於訓練統計資訊,我們設定了tutorial_files資料夾。 如果您已經執行本教程,那麼您應該已經擁有這些資料夾。 執行以下單元格時,將確保在tutorial_data資料夾中存在MNIST資料集的訓練和測試lmdb資料庫。

# This section preps your image and test set in a lmdb database
def DownloadResource(url, path):
    '''Downloads resources from s3 by url and unzips them to the provided path'''
    import requests, zipfile, StringIO
    print("Downloading... {} to {}".format(url, path))
    r = requests.get(url, stream=True)
    z = zipfile.ZipFile(StringIO.StringIO(r.content))
    z.extractall(path)
    print("Completed download and extraction.")
    
# Setup the paths for the necessary directories 
current_folder = os.path.join(os.path.expanduser('~'), 'caffe2_notebooks')
data_folder = os.path.join(current_folder, 'tutorial_data', 'mnist')
root_folder = os.path.join(current_folder, 'tutorial_files', 'tutorial_mnist')
db_missing = False

# Check if the data folder already exists
if not os.path.exists(data_folder):
    os.makedirs(data_folder)   
    print("Your data folder was not found!! This was generated: {}".format(data_folder))

# Check if the training lmdb exists in the data folder
if os.path.exists(os.path.join(data_folder,"mnist-train-nchw-lmdb")):
    print("lmdb train db found!")
else:
    db_missing = True
    
# Check if the testing lmdb exists in the data folder   
if os.path.exists(os.path.join(data_folder,"mnist-test-nchw-lmdb")):
    print("lmdb test db found!")
else:
    db_missing = True

# Attempt the download of the db if either was missing
if db_missing:
    print("one or both of the MNIST lmbd dbs not found!!")
    db_url = "http://download.caffe2.ai/databases/mnist-lmdb.zip"
    try:
        DownloadResource(db_url, data_folder)
    except Exception as ex:
        print("Failed to download dataset. Please download it manually from {}".format(db_url))
        print("Unzip it and place the two database folders here: {}".format(data_folder))
        raise ex

# Clean up statistics from any old runs
if os.path.exists(root_folder):
    print("Looks like you ran this before, so we need to cleanup those old files...")
    shutil.rmtree(root_folder)
    
os.makedirs(root_folder)
workspace.ResetWorkspace(root_folder)

print("training data folder:" + data_folder)
print("workspace root folder:" + root_folder)

執行出現下面結果:

 

 如果在最後一步中找不到資料庫,請下載 download the MNIST lmdb database 或檢視有關如何從MNIST資料集建立資料庫的資料集和資料庫筆記本  datasets and databases notebook 

模型構建
我們將使用ModelHelper類來表示我們的主模型,並使用brew模組以及其他普通的Caffe2運算子來構建我們的模型。 ModelHelper是一個特殊的類,它儲存有關引數初始化,網路結構,引數名稱以及稍後對映到漸變的大量資訊。 我們將在下面的其他地方看到它如何與brew一起使用。
為避免混淆,請注意model.MyOperator是model.net.MyOperator的語法糖,它將相應的MyOperator運算子新增到model.net。

brew 簡介
brew是一組helper 函式,旨在簡化向我們的模型新增複雜邏輯的過程(有關更多資訊,請單擊此處)。例如,當我們想要新增引數初始化以及計算步驟時,brew會派上用場。現在,讓我們更詳細地探討這個問題。
brew模組具有一組包裝函式,可自動將引數初始化和實際計算分成兩個網路。在引擎蓋下,ModelHelper物件有兩個底層網路param_init_net和net,它們分別記錄初始化網路和主網路。 model.params還跟蹤引數名稱。
高階流程
為了模組化,我們將模型的構造分為不同的部分:
(1)資料輸入部分(AddInput函式)
(2)主要計算部分(AddModel函式)
(3)訓練部分 - 新增梯度運算元,優化演算法等(AddTrainingOperators函式)
(4)bookkeeping 部分,我們只打印出統計檢查。 (AddBookkeepingOperators函式)

新增輸入
AddInput將從DB載入資料(特別是上面下載的lmdbs)。載入的MNIST資料以NCHW順序儲存為畫素值,因此在批處理後,這將為我們提供形狀[batch_size,num_channels,width,height]的資料。更具體地說,由於我們的MNIST資料是尺寸為28x28px的8位(uint8)灰度影象,因此我們作為網路輸入的資料形狀為[batch_size,1,28,28],我們的標籤型別為int with shape [batch_size] 。
由於我們要進行浮點計算,我們將把資料轉換為浮點資料型別。為了獲得更好的數值穩定性,我們將其縮小為[0,1],而不是在[0,255]範圍內表示資料。請注意,我們正在為此運算子進行就地計算,因為我們不需要預先縮放的資料。此外,在計算反向傳遞時,我們應該指定我們不需要資料準備部分的梯度計算。 StopGradient就是這樣做的:在前向傳遞中它什麼都不做,而在後向傳遞中它所做的就是告訴梯度生成器“漸變不需要通過這裡”。

def AddInput(model, batch_size, db, db_type):
    ### load the data from db - Method 1 using brew
    #data_uint8, label = brew.db_input(
    #    model,
    #    blobs_out=["data_uint8", "label"],
    #    batch_size=batch_size,
    #    db=db,
    #    db_type=db_type,
    #)
    ### load the data from db - Method 2 using TensorProtosDB
    data_uint8, label = model.TensorProtosDBInput(
        [], ["data_uint8", "label"], batch_size=batch_size,
        db=db, db_type=db_type)
    # cast the data to float
    data = model.Cast(data_uint8, "data", to=core.DataType.FLOAT)
    # scale data from [0,255] down to [0,1]
    data = model.Scale(data, data, scale=float(1./256))
    # don't need the gradient for the backward pass
    data = model.StopGradient(data, data)
    return data, label

 MLP模型定義
注意:這是標誌USE_LENET_MODEL = False時使用的模型
現在我們要構建自己的模型。 輸入將是我們的資料blob(X
  並且輸出將是長度為10的向量,其包含網路對MNIST中的10個可能數字中的每一個的預測。
在這個多層感知器(MLP)架構中,將使用ReLU啟用功能,並定義為:

 請看上面圖片公式。

回想一下,MLP的每一層只是一些輸入資料和權重矩陣之間的矩陣乘法(W),然後新增偏見(b)。 在機器學習中,這是通過一個完全連線的層實現的,該層在Caffe2中恰當地命名為FC。 然後將該乘法的結果傳遞給非線性啟用函式(ReLU)。 每個啟用函式的輸出作為輸入資料傳遞到下一層,並通過各層重複此過程,如下所示:

請看上面的圖片公式。

一旦資料在前向傳遞中通過網路傳播,我們將使用Softmax運算子將每個數字的分數轉換為概率。 通常,分數總和為1(即p0 + ... + p9 = 1.0 並且沒有單個分數大於或等於1(即0 <= pi <= 1.0)。 可以在此處 http://deeplearning.net/tutorial/mlp.html 找到MLP的更詳細描述。
在這個功能中,我們將首次使用Brew。 在引擎蓋下,當我們呼叫brew.fc(model,layer,...)時,會發生以下情況:
通過呼叫model.net.FC([layer,W,b],...)將FC運算子新增到model.net W的初始化 和b被新增到model.param_init_net
希望這使得brew的便利性更加清晰!

# Function to construct a MLP neural network
# The input 'model' is a model helper and 'data' is the input data blob's name
def AddMLPModel(model, data):
    size = 28 * 28 * 1
    sizes = [size, size * 2, size * 2, 10]
    layer = data
    for i in range(len(sizes) - 1):
        layer = brew.fc(model, layer, 'dense_{}'.format(i), dim_in=sizes[i], dim_out=sizes[i + 1])
        layer = brew.relu(model, layer, 'relu_{}'.format(i))
    softmax = brew.softmax(model, layer, 'softmax')
    return softmax

 

LeNet模型定義
注意:這是標誌USE_LENET_MODEL = True時使用的模型
下面是另一個可能(並且非常強大)的架構,稱為LeNet。 與MLP模型的主要區別在於LeNet是卷積神經網路(CNN),因此使用卷積層(Conv),最大池化層 (MaxPool), ReLUs,和完全連線(FC)層。 有關CNN如何工作的完整解釋超出了本教程的範圍,但這裡有一些好的資源供好奇的讀者使用:

注意,此功能也使用Brew。 但是,這次我們新增的不僅僅是FC和Softmax圖層。

def AddLeNetModel(model, data):
    '''
    This part is the standard LeNet model: from data to the softmax prediction.
    
    For each convolutional layer we specify dim_in - number of input channels
    and dim_out - number or output channels. Also each Conv and MaxPool layer changes the
    image size. For example, kernel of size 5 reduces each side of an image by 4.

    While when we have kernel and stride sizes equal 2 in a MaxPool layer, it divides
    each side in half.
    '''
    # Image size: 28 x 28 -> 24 x 24
    conv1 = brew.conv(model, data, 'conv1', dim_in=1, dim_out=20, kernel=5)
    # Image size: 24 x 24 -> 12 x 12
    pool1 = brew.max_pool(model, conv1, 'pool1', kernel=2, stride=2)
    # Image size: 12 x 12 -> 8 x 8
    conv2 = brew.conv(model, pool1, 'conv2', dim_in=20, dim_out=50, kernel=5)
    # Image size: 8 x 8 -> 4 x 4
    pool2 = brew.max_pool(model, conv2, 'pool2', kernel=2, stride=2)
    # 50 * 4 * 4 stands for dim_out from previous layer multiplied by the image size
    # Here, the data is flattened from a tensor of dimension 50x4x4 to a vector of length 50*4*4
    fc3 = brew.fc(model, pool2, 'fc3', dim_in=50 * 4 * 4, dim_out=500)
    relu3 = brew.relu(model, fc3, 'relu3')
    # Last FC Layer
    pred = brew.fc(model, relu3, 'pred', dim_in=500, dim_out=10)
    # Softmax Layer
    softmax = brew.softmax(model, pred, 'softmax')
    
    return softmax

下面的AddModel函式允許我們輕鬆地從MLP切換到LeNet模型。 只需更改筆記本頂部的USE_LENET_MODEL,然後重新執行整個過程。

def AddModel(model, data):
    if USE_LENET_MODEL:
        return AddLeNetModel(model, data)
    else:
        return AddMLPModel(model, data)

準確度層
下面的AddAccuracy函式為模型添加了精度運算子。 它使用softmax分數和輸入訓練標籤(記住這是一種監督學習技術)來報告當前訓練批次的準確度分數。 通常不建議在訓練期間對準確度分數過度興奮,因為該值沒有給出關於泛化效能的指示,但是訓練準確性確實給出了模型是否正在學習的想法。 通過培訓過程跟蹤準確性也將讓我們建立一個很好的情節來總結培訓執行。

def AddAccuracy(model, softmax, label):
    """Adds an accuracy op to the model"""
    accuracy = brew.accuracy(model, [softmax, label], "accuracy")
    return accuracy

新增培訓操作員
下一個函式AddTrainingOperators將訓練操作符新增到模型中。 請按照內聯評論瞭解所有步驟。 總而言之,這是我們指定模型如何計算損失以及特定優化演算法及其超引數的地方。 在本教程中,我們將使用vanilla SGD演算法,該演算法使用優化器模組中的build_sgd輔助函式構建。 作為此函式的結果,模型物件包含諸如引數名稱(model.param)之類的資訊以及從引數名稱到相應漸變的對映。

def AddTrainingOperators(model, softmax, label):
    """Adds training operators to the model."""
    # Compute cross entropy between softmax scores and labels
    xent = model.LabelCrossEntropy([softmax, label], 'xent')
    # Compute the expected loss
    loss = model.AveragedLoss(xent, "loss")
    # Track the accuracy of the model
    AddAccuracy(model, softmax, label)
    # Use the average loss we just computed to add gradient operators to the model
    model.AddGradientOperators([loss])
    # Specify the optimization algorithm
    optimizer.build_sgd(
        model,
        base_learning_rate=0.1,
        policy="step",
        stepsize=1,
        gamma=0.999,
    )

新增Bookkeeping(簿記)  Operators(操作符)
以下函式AddBookkeepingOperations添加了一些我們稍後可以檢查的簿記操作符。 這些運算子不會影響訓練過程:它們僅收集統計資訊並將其列印到檔案或日誌中。 因此,這不是必要的步驟,但記賬將使我們能夠在培訓結束後審查培訓結果。

def AddBookkeepingOperators(model):
    """This adds a few bookkeeping operators that we can inspect later.
    
    These operators do not affect the training procedure: they only collect
    statistics and prints them to file or to logs.
    """    
    # Print basically prints out the content of the blob. to_file=1 routes the
    # printed output to a file. The file is going to be stored under
    #     root_folder/[blob name]
    model.Print('accuracy', [], to_file=1)
    model.Print('loss', [], to_file=1)
    # Summarizes the parameters. Different from Print, Summarize gives some
    # statistics of the parameter, such as mean, std, min and max.
    for param in model.params:
        model.Summarize(param, [], to_file=1)
        model.Summarize(model.param_to_grad[param], [], to_file=1)
    # Now, if we really want to be verbose, we can summarize EVERY blob
    # that the model produces; it is probably not a good idea, because that
    # is going to take time - summarization do not come for free. For this
    # demo, we will only show how to summarize the parameters and their
    # gradients.

構建模型
現在我們已經定義了函式,讓我們實際建立用於訓練,測試和模擬部署的模型。 如果您在下面看到警告訊息,請不要驚慌。 我們之前建立的功能現在將被執行。 請記住我們正在做的四個步驟:

    (1) data input    資料輸入
    (2) main computation  主要計算
    (3) training  訓練
    (4) bookkeeping 簿記

#### Train Model
# Specify the data will be input in NCHW order
#  (i.e. [batch_size, num_channels, height, width])
arg_scope = {"order": "NCHW"}
# Create the model helper for the train model
train_model = model_helper.ModelHelper(name="mnist_train", arg_scope=arg_scope)
# Specify the input is from the train lmdb
data, label = AddInput(
    train_model, batch_size=64,
    db=os.path.join(data_folder, 'mnist-train-nchw-lmdb'),
    db_type='lmdb')
# Add the model definition (fc layers, conv layers, softmax, etc.)
softmax = AddModel(train_model, data)
# Add training operators, specify loss function and optimization algorithm
AddTrainingOperators(train_model, softmax, label)
# Add bookkeeping operators to save stats from training
AddBookkeepingOperators(train_model)

#### Testing model. 
# We will set the batch size to 100, so that the testing
#   pass is 100 iterations (10,000 images in total).
#   For the testing model, we need the data input part, the main AddModel
#   part, and an accuracy part. Note that init_params is set False because
#   we will be using the parameters obtained from the train model which will
#   already be in the workspace.
test_model = model_helper.ModelHelper(
    name="mnist_test", arg_scope=arg_scope, init_params=False)
data, label = AddInput(
    test_model, batch_size=100,
    db=os.path.join(data_folder, 'mnist-test-nchw-lmdb'),
    db_type='lmdb')
softmax = AddModel(test_model, data)
AddAccuracy(test_model, softmax, label)

#### Deployment model. 
# We simply need the main AddModel part.
deploy_model = model_helper.ModelHelper(
    name="mnist_deploy", arg_scope=arg_scope, init_params=False)
AddModel(deploy_model, "data")

輸出:BlobReference("softmax")

Visualize our Progress 
現在,讓我們看一下使用Caffe2具有的簡單圖形視覺化工具,培訓和部署模型的樣子。 如果以下命令失敗,可能是因為您的計算機沒有安裝graphviz。 您需要通過您選擇的包管理器安裝它。
如果圖形看起來太小,請右鍵單擊並在新選項卡中開啟影象以便更好地檢查。

from IPython import display
graph = net_drawer.GetPydotGraph(train_model.net.Proto().op, "mnist", rankdir="LR")
display.Image(graph.create_png(), width=800)

現在,上圖顯示了培訓階段發生的所有事情:白色節點是blob,綠色矩形節點是正在執行的運算子。 您可能已經注意到像火車軌道這樣的大規模平行線:這些是從前向傳遞中生成的blob到其後向運算子的依賴關係。
讓我們通過僅顯示必要的依賴關係並僅顯示運算子來以更小的方式顯示圖形。 如果仔細閱讀,可以看到圖的左半部分是向前傳遞,圖的右半部分是向後傳遞,右邊是一組引數更新和彙總運算子。

graph = net_drawer.GetPydotGraphMinimal(
    train_model.net.Proto().op, "mnist", rankdir="LR", minimal_dependency=True)
display.Image(graph.create_png(), width=800)

輸出:

 

 

重要的是在這裡重申ModelHelper類尚未執行任何操作的事實。 到目前為止我們所做的就是宣告網路,它基本上建立了協議緩衝區

指定的計算圖。 以下是我們如何為訓練模型的主網路和引數初始化網路顯示部分序列化協議緩衝區的示例。

print("*******train_model.net.Proto()*******\n")
print(str(train_model.net.Proto())[:400] + '\n...')
print("\n*******train_model.param_init_net.Proto()*******\n")
print(str(train_model.param_init_net.Proto())[:400] + '\n...')
輸出的結果:
*******train_model.net.Proto()*******

name: "mnist_train"
op {
  input: "dbreader_/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb"
  output: "data_uint8"
  output: "label"
  name: ""
  type: "TensorProtosDBInput"
  arg {
    name: "batch_size"
    i: 64
  }
}
op {
  input: "data_uint8"
  output: "data"
  name: ""
  type: "Cast"
  arg {
    name: "to"
    i: 1
  }
}
op {
  input: "data"
  output: "data
...

*******train_model.param_init_net.Proto()*******

name: "mnist_train_init"
op {
  output: "dbreader_/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb"
  name: ""
  type: "CreateDB"
  arg {
    name: "db_type"
    s: "lmdb"
  }
  arg {
    name: "db"
    s: "/Users/nathaninkawhich/caffe2_notebooks/tutorial_data/mnist/mnist-train-nchw-lmdb"
  }
}
op {
  output: "conv1_w"
  name: ""
  type: "XavierFill"
  arg {
    na
...

Run Training
建立我們的模型並指定資料來源後,我們現在可以執行培訓程式。請注意,此過程可能需要一段時間才能執行。密切注意程式碼塊仍在執行的星號(In [*])或其他IPython指示器。
我們通過連續多次執行我們的網路來進行培訓,每次都在一批新的輸入上進行。請注意,在此過程中,我們可以獲取工作空間中任何blob的值,包括準確性和丟失。這使我們能夠建立訓練圖並定期監測訓練的準確性和損失。此外,一旦模型看到所有資料一次(1個紀元),訓練就不會停止,而是一直持續到達到指定的迭代次數。
使用MLP時,模型精度很大程度上取決於引數的隨機初始化和訓練迭代次數。如果您的模型停留在大約50%的準確度,請重新執行筆記本,這將從另一個隨機種子和新引數初始化開始。 

# The parameter initialization network only needs to be run once.
# Now all the parameter blobs are initialized in the workspace.
workspace.RunNetOnce(train_model.param_init_net)

# Creating an actual network as a C++ object in memory.
#   We need this as the object is going to be used a lot
#   so we avoid creating an object every single time it is used.
# overwrite=True allows you to run this cell several times and avoid errors
workspace.CreateNet(train_model.net, overwrite=True)

# Set the iterations number and track the accuracy & loss
total_iters = 200
accuracy = np.zeros(total_iters)
loss = np.zeros(total_iters)

# MAIN TRAINING LOOP!
# Now, we will manually run the network for 200 iterations. 
for i in range(total_iters):
    workspace.RunNet(train_model.net)
    accuracy[i] = workspace.blobs['accuracy']
    loss[i] = workspace.blobs['loss']
    # Check the accuracy and loss every so often
    if i % 25 == 0:
        print("Iter: {}, Loss: {}, Accuracy: {}".format(i,loss[i],accuracy[i]))

# After the execution is done, let's plot the values.
pyplot.plot(loss, 'b')
pyplot.plot(accuracy, 'r')
pyplot.title("Summary of Training Run")
pyplot.xlabel("Iteration")
pyplot.legend(('Loss', 'Accuracy'), loc='upper right')

輸出:

 

346/5000

視覺化結果
現在我們的模型已經過培訓,我們可以看到我們的工作成果。 但首先,檢視上面訓練期間列印的內容。 在第一次迭代中,批次0的訓練精度為3.1%,但是在第100次迭代中它很快收斂到九十年代中期。 情節也顯示了這種趨勢。
這是一個好兆頭!

### Let's look at some of the training data
pyplot.figure()
pyplot.title("Training Data Sample")
# Grab the most recent data blob (i.e. batch) from the workspace
data = workspace.FetchBlob('data')
# Use visualize module to show the examples from the last batch that was fed to the model
_ = visualize.NCHW.ShowMultiple(data)

### Let's visualize the softmax result
pyplot.figure()
pyplot.title('Softmax Prediction for the first image above')
pyplot.ylabel('Confidence')
pyplot.xlabel('Label')
# Grab and visualize the softmax blob for the batch we just visualized. Since batch size
#  is 64, the softmax blob contains 64 vectors, one for each image in the batch. To grab
#  the vector for the first image, we can simply index the fetched softmax blob at zero.
softmax = workspace.FetchBlob('softmax')
_ = pyplot.plot(softmax[0], 'ro')

輸出:

視覺化學習卷積濾波器
注意:這僅適用於使用LeNet模型的情況(USE_LENET_MODEL = True)
對於卷積模型,我們也可以看到它們如何“思考”,即它們提出了哪些特徵。 我們不是取出對人類不太有意義的學習權重,而是獲取在輸入上對這些權重進行卷積的結果。 在這裡,我們獲取第一個卷積層conv1的輸出特徵對映,這是將第一層濾波器與最新訓練資料批量進行卷積的結果。 請注意,如果在評估階段後重新執行此程式碼,則最後一個小批量將更改,因為評估和培訓共享相同的工作區。

if USE_LENET_MODEL:
    pyplot.figure()
    pyplot.title("Conv1 Output Feature Maps for Most Recent Mini-batch")
    # Grab the output feature maps of conv1. Change this to conv2 in order to look into the second one.
    #  Remember, early convolutional layers tend to learn human-interpretable features but later conv
    #  layers work with highly-abstract representations. For this reason, it may be harder to understand
    #  features of the later conv layers.
    conv = workspace.FetchBlob('conv1')
    
    # We can look into any channel. Think of it as a feature model learned.
    # In this case we look into the 5th channel. Play with other channels to see other features
    conv = conv[:,[5],:,:]

    _ = visualize.NCHW.ShowMultiple(conv)

輸出:

測試模型
還記得我們建立了一個測試網嗎? 現在我們已經訓練了模型,並且我們的工作空間包含訓練的模型引數,我們可以對先前看不見的測試資料執行測試傳遞,以測試精度統計的形式檢查我們的泛化效能。 雖然test_model將使用從train_model獲得的引數,但仍必須執行test_model.param_init_net來初始化輸入資料。
我們只會執行100次迭代,因為我們有10,000個測試影象,而test_model的批量大小為100.此外,我們將報告批量精確度的平均值,而不是報告每個單獨影象的準確度分數。

# param_init_net here will only create a data reader
# Other parameters won't be re-created because we selected init_params=False before
workspace.RunNetOnce(test_model.param_init_net)
workspace.CreateNet(test_model.net, overwrite=True)

# Testing Loop 
test_accuracy = np.zeros(100)
for i in range(100):
    # Run a forward pass of the net on the current batch
    workspace.RunNet(test_model.net)
    # Collect the batch accuracy from the workspace
    test_accuracy[i] = workspace.FetchBlob('accuracy')
    
# After the execution is done, let's plot the accuracy values.
pyplot.plot(test_accuracy, 'r')
pyplot.title('Accuracy over test batches.')
print('test_accuracy: %f' % test_accuracy.mean())

輸出:

部署模型
儲存部署模型
讓我們使用經過訓練的權重和偏差將部署模型儲存到檔案中。 希望這一步驟的重要性是顯而易見的,但這將使我們能夠在另一個時間執行模型來執行更多測試,而不是每次都必須從頭開始訓練模型。 當訓練的引數仍在工作空間中時,必須在訓練後立即執行此步驟。

# construct the model to be exported
# the inputs/outputs of the model are manually specified.
pe_meta = pe.PredictorExportMeta(
    predict_net=deploy_model.net.Proto(),
    parameters=[str(b) for b in deploy_model.params], 
    inputs=["data"],
    outputs=["softmax"],
)

# save the model to a file. Use minidb as the file format
pe.save_to_db("minidb", os.path.join(root_folder, "mnist_model.minidb"), pe_meta)
print("Deploy model saved to: " + root_folder + "/mnist_model.minidb")
Deploy model saved to: /Users/nathaninkawhich/caffe2_notebooks/tutorial_files/tutorial_mnist/mnist_model.minidb

載入已儲存的模型
注意:為了本教程,我們將在此處執行此操作,但此部分表示我們如何在另一個地方載入經過訓練和儲存的模型。
讓我們從上一步載入已儲存的部署模型,然後重新執行我們的測試以進行驗證。

# Grab and display the last data batch used before we scratch the workspace. This purely for our convenience...
blob = workspace.FetchBlob("data")
pyplot.figure()
pyplot.title("Batch of Testing Data")
_ = visualize.NCHW.ShowMultiple(blob)

# reset the workspace, to make sure the model is actually loaded
workspace.ResetWorkspace(root_folder)

# verify that all blobs from training are destroyed. 
print("The blobs in the workspace after reset: {}".format(workspace.Blobs()))

# load the predict net
predict_net = pe.prepare_prediction_net(os.path.join(root_folder, "mnist_model.minidb"), "minidb")

# verify that blobs are loaded back
print("The blobs in the workspace after loading the model: {}".format(workspace.Blobs()))

# feed the previously saved data to the loaded model
workspace.FeedBlob("data", blob)

# predict
workspace.RunNetOnce(predict_net)
softmax = workspace.FetchBlob("softmax")

print("Shape of softmax: ",softmax.shape)

# Quick way to get the top-1 prediction result
# Squeeze out the unnecessary axis. This returns a 1-D array of length 10
# Get the prediction and the confidence by finding the maximum value and index of maximum value in preds array
curr_pred, curr_conf = max(enumerate(softmax[0]), key=operator.itemgetter(1))
print("Prediction: ", curr_pred)
print("Confidence: ", curr_conf)

# the first letter should be predicted correctly
pyplot.figure()
pyplot.title('Prediction for the first image')
pyplot.ylabel('Confidence')
pyplot.xlabel('Label')
_ = pyplot.plot(softmax[0], 'ro')

輸出: 

 

這是MNIST教程的結束。 我們希望本教程重點介紹Caffe2的一些功能以及建立簡單MLP或CNN模型的難易程度。