DL4J中文文件/模型/計算圖
用計算圖構建複雜網路架構
本頁描述瞭如何使用 DL4J的計算圖功能來構建更復雜的網路。
內容
- 計算圖概述
- 計算圖:一些用例
- 配置一個計算圖網路
- 圖頂點型別
- 示例1:具有跳過連線的迴圈網路
- 示例2:多個輸入和合並頂點
- 例3:多工學習
- 自動新增前處理器和計算nIns
- 用於計算圖的訓練資料
- RecordReaderMultiDataSetIterator 示例 1: 迴歸資料
- RecordReaderMultiDataSetIterator 示例 2: 分類和多工學習
計算圖概述
DL4J有兩種型別的包括多個層的網路:
- MultiLayerNetwork(多層網路),是神經網路層棧必不可少的(具有單個輸入層和單個輸出層)。
- ComputationGraph (計算圖), 它允許在網路架構中獲得更大的自由度。
具體而言,計算圖允許構建具有以下特徵的網路:
- 多網路輸入陣列
- 多個網路輸出(包括混合分類/迴歸加構)
- 使用有向無環圖連線結構連線到其他層的層(而不是一個層棧)
一般說來,當構建具有單個輸入層、單個輸出層和 輸入->a->b->c->輸出 型別連線結構的網路時:多層網路通常是首選網路。然而,MultiLayerNetwork所能做的一切,ComputationGraph也可以做到——儘管配置可能稍微複雜一些。
計算圖: 一些用例
可以使用計算圖構建的一些架構的示例包括:
- 多作務學習架構
- 具有跳躍連線的迴圈神經網路
- GoogLeNet,一種用於影象分類的複雜卷積網路
- 影象標註建立
- 卷積網路在句子分類中的應用
- 殘差學習卷積神經網路
配置一個計算圖
頂點的型別
其基本思想是,在計算圖中,核心構建塊是圖形頂點,而不是層。層(或者,更準確地說,是層頂點物件),只是圖中的一種頂點。其他型別的頂點包括:
- 輸入頂點
- 元素運算頂點
- 合併頂點
- 子集頂點
- 前處理器頂點
下面簡要描述這些型別的圖形頂點。
LayerVertex: 層頂點(具有神經網路層的圖形頂點)用.addLayer(String,Layer,String...)方法
被新增。第一個引數是層的標籤,最後的引數是該層的輸入。如果需要手動新增InputPreProcessor(通常不需要,請參閱下一節),可以使用.addLayer(String、Layer、InputPreProcessor、String...)方法。
InputVertex: 輸入頂點由你配置中的addInputs(String...)
方法指定。用作輸入的字串可以是任意的——它們是使用者定義的標籤,並且可以在以後的配置中引用。提供的字串數量定義了輸入的數量;輸入的順序還在fit方法(或DataSet/MultiDataSet物件)中定義了相應的INDArray的順序。
ElementWiseVertex: 元素操作頂點執行例如從一個或多個其他頂點對啟用進行元素式加法或減法。因此,用作ElementWiseVertex的輸入的啟用必須都具有相同的大小,並且元素頂點的輸出大小與輸入相同。
MergeVertex: 合併頂點 聯接/合併輸入啟用。例如,如果一個合併頂點分別具有2個大小為5和10的輸入,則輸出大小將是5 + 10=15啟用。對於卷積網路啟用,示例沿著深度合併:因此假設來自一個層的啟用具有4個特徵,而另一個具有5個特徵(都具有(4或5)x寬度x高度啟用),那麼輸出將具有(4+5)x寬度x高度啟用。
SubsetVertex: 子集頂點允許你只從另一個頂點獲得啟用的一部分。例如,為了從標籤為“layer1”的另一個頂點獲得前5個啟用,可以使用.addVertex("subset1", new SubsetVertex(0,4), "layer1")
:這意味著“layer1”頂點中的第0至第4(包含)個啟用將被用作子集頂點的輸出。
PreProcessorVertex: 有時,你可能希望InputPreProcessor的功能不與層相關聯。PreProcessorVertex頂點允許你這樣做。
最後,也可以為你自定義的圖頂點通過實現一個configuration 和 implementation 類實現自定義圖頂點。
示例1:具有跳過連線的迴圈網路
假設我們希望建立以下迴圈神經網路體系架構:
為了這個例子,假設我們的輸入資料的大小是5。我們的配置如下:
ComputationGraphConfiguration conf = new NeuralNetConfiguration.Builder()
.updater(new Sgd(0.01))
.graphBuilder()
.addInputs("input") //這裡可以使用任意的標籤
.addLayer("L1", new GravesLSTM.Builder().nIn(5).nOut(5).build(), "input")
.addLayer("L2",new RnnOutputLayer.Builder().nIn(5+5).nOut(5).build(), "input", "L1")
.setOutputs("L2") //我們需要指定網路輸出和它們的順序。
.build();
ComputationGraph net = new ComputationGraph(conf);
net.init();
注意,在.addLayer(...)方法中,第一個字串("L1"、"L2")是該層的名稱,而結尾的字串(["input"]、["input"、"L1"])是該層的輸入。
示例2:多個輸入和合並頂點
考慮下面的架構:
這裡,合併頂點從層L1和L2取出啟用,併合並(連線)它們:因此,如果層L1和L2都具有4個輸出啟用(.nOut(4)),則合併頂點的輸出大小是4+4=8個啟用。
為了構建上述網路,我們使用以下配置:
ComputationGraphConfiguration conf = new NeuralNetConfiguration.Builder()
.updater(new Sgd(0.01))
.graphBuilder()
.addInputs("input1", "input2")
.addLayer("L1", new DenseLayer.Builder().nIn(3).nOut(4).build(), "input1")
.addLayer("L2", new DenseLayer.Builder().nIn(3).nOut(4).build(), "input2")
.addVertex("merge", new MergeVertex(), "L1", "L2")
.addLayer("out", new OutputLayer.Builder().nIn(4+4).nOut(3).build(), "merge")
.setOutputs("out")
.build();
例3:多工學習
在多工學習中,使用神經網路進行多個獨立的預測。例如,考慮同時用於分類和迴歸的簡單網路。在這種情況下,我們有兩個輸出層,“out1”用於分類,和“out2”迴歸。
在這種情況下,網路配置是:
ComputationGraphConfiguration conf = new NeuralNetConfiguration.Builder()
.updater(new Sgd(0.01))
.graphBuilder()
.addInputs("input")
.addLayer("L1", new DenseLayer.Builder().nIn(3).nOut(4).build(), "input")
.addLayer("out1", new OutputLayer.Builder()
.lossFunction(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nIn(4).nOut(3).build(), "L1")
.addLayer("out2", new OutputLayer.Builder()
.lossFunction(LossFunctions.LossFunction.MSE)
.nIn(4).nOut(2).build(), "L1")
.setOutputs("out1","out2")
.build();
自動新增前處理器和計算nIns
ComputationGraphConfiguration的一個特性是,你可以使用配置中的.setInputTypes(InputType...)
方法來指定網路的輸入型別。
setInputType 有兩個作用:
- 它將根據需要自動新增任何輸入前處理器。輸入前處理器對於處理例如全連線(密連)層和卷積層,或者迴圈和全連線層之間的互動是必需的。
- 它將自動計算一個層的輸入數(.nin(x)配置)。因此,如果你使用的是setInputTypes(InputType...)功能,則無需手動指定配置中的.nIn(x)選項。這可以簡化構建一些架構(例如具有完全連線層的卷積網路)。如果為層指定了.nIn(x),則當使用InputType功能時,網路將不覆蓋此。
例如,如果你的網路有2個輸入,一個是卷積輸入,另一個是前饋輸入,那麼你將使用.setInputTypes(InputType.convolutional(depth,width,height), InputType.feedForward(feedForwardInputSize))。
為計算圖訓練資料
有兩種型別的資料可以與計算圖一起使用。
DataSet 與 DataSetIterator
DataSet類最初是為與MultiLayerNetwork一起使用而設計的,但是也可以與ComputationGraph一起使用,但前提是該計算圖具有單個輸入和輸出陣列。對於具有多個輸入陣列或多個輸出陣列的計算圖架構,不能使用DataSet和DataSetIterator(相反,使用MultiDataSet/MultiDataSetIterator)。
DataSet物件基本上是一對容納你的訓練資料的INDArray 。在RNNs的情況下,它也可以包括掩蔽陣列(參見這個詳細資訊)。DataSetIterator本質上是DataSet物件上的迭代器。
MultiDataSet 與 MultiDataSetIterator
MultiDataSet是DataSet的多輸入和/或多輸出版本。在神經網路的情況下,它還可以包括多個掩模陣列(對於每個輸入/輸出陣列)。作為一般規則,除非使用多個輸入和/或多個輸出,否則應使用DataSet/DataSetIterator。
當前有兩種方式使用MultiDataSetIterator:
- 通過直接實現 MultiDataSetIterator 介面
- 與DataVec記錄讀取器結合使用RecordReaderMultiDataSetIterator
RecordReaderMultiDataSetIterator提供了多個載入資料的選項。特別地,RecordReaderMultiDataSetIterator提供以下功能:
- 可以同時使用多個DataVec記錄讀取器
- 記錄讀取器不必是相同的模式:例如,可以把CSV記錄讀取器與影象記錄讀取器一起使用。
- 可以出於不同的目的使用RecordReader中的列的子集——例如,CSV中的前10列可以是你的輸入,而後5列可以是你的輸出。
- 將單個列從類索引轉換為one-hot表示是可能的。
如下是如何使用 RecordReaderMultiDataSetIterator 的示例。你可能也能找到這些有用的單元測試。
RecordReaderMultiDataSetIterator 示例1:迴歸資料
Suppose we have a CSV file with 5 columns, and we want to use the first 3 as our input, and the last 2 columns as our output (for regression). We can build a MultiDataSetIterator to do this as follows:
int numLinesToSkip = 0;
String fileDelimiter = ",";
RecordReader rr = new CSVRecordReader(numLinesToSkip,fileDelimiter);
String csvPath = "/path/to/my/file.csv";
rr.initialize(new FileSplit(new File(csvPath)));
int batchSize = 4;
MultiDataSetIterator iterator = new RecordReaderMultiDataSetIterator.Builder(batchSize)
.addReader("myReader",rr)
.addInput("myReader",0,2) //Input: columns 0 to 2 inclusive
.addOutput("myReader",3,4) //Output: columns 3 to 4 inclusive
.build();
RecordReaderMultiDataSetIterator 例2:分類和多工學習
假設我們有兩個單獨的CSV檔案,一個用於我們的輸入,一個用於我們的輸出。進一步假設我們正在構建一個多工學習架構,其中有兩個輸出-一個用於分類。對於這個例子,假設資料如下:
- 輸入檔案:myInput.csv,我們希望使用所有列作為輸入(沒有修改)
- 輸出檔案: myOutput.csv.
- 網路輸入1 - 迴歸: 列 0 到 3
- 網路輸出 2 - 分類: 列 4是分類索引,有3個類。因此,列4只包含整數值[0,1,2],我們希望將這些索引轉換為one-hot表示以進行分類。
在這種情況下,我們可以構建如下的迭代器:
int numLinesToSkip = 0;
String fileDelimiter = ",";
RecordReader featuresReader = new CSVRecordReader(numLinesToSkip,fileDelimiter);
String featuresCsvPath = "/path/to/my/myInput.csv";
featuresReader.initialize(new FileSplit(new File(featuresCsvPath)));
RecordReader labelsReader = new CSVRecordReader(numLinesToSkip,fileDelimiter);
String labelsCsvPath = "/path/to/my/myOutput.csv";
labelsReader.initialize(new FileSplit(new File(labelsCsvPath)));
int batchSize = 4;
int numClasses = 3;
MultiDataSetIterator iterator = new RecordReaderMultiDataSetIterator.Builder(batchSize)
.addReader("csvInput", featuresReader)
.addReader("csvLabels", labelsReader)
.addInput("csvInput") //輸入: 來自讀取器的所有列
.addOutput("csvLabels", 0, 3) //輸出 1: 列 0 到 3 包括3
.addOutputOneHot("csvLabels", 4, numClasses) //輸出 2: 列4 -> 為分類轉換為one-hot
.build();