Deeplearning4j 實戰 (14):基於SameDiff自動微分工具的線性迴歸建模

Eclipse Deeplearning4j GitChat課程https://gitbook.cn/gitchat/column/5bfb6741ae0e5f436e35cd9f
Eclipse Deeplearning4j 系列博客https://blog.csdn.net/wangongxi
Eclipse Deeplearning4j Githubhttps://github.com/eclipse/deeplearning4j

Eclipse Deeplearning4j 1.0.0-alpha之前的版本只支持基於Layer的建模方式,而從1.0.0-alpha版本之後(包括該版本),Skymind團隊在ND4j(https://deeplearning4j.org/docs/latest/nd4j-overview)框架中添加了對自動微分引擎SameDiff的支持,提供了大量神經網絡的常用算子,因此開發人員可以基於這些OP構建神經網絡計算圖,從而完成深度學習的建模工作。本質上,基於Layer的建模方式是對算子的封裝,對於初學者來說更加清晰,神經網絡結構可以一目瞭然,但是其缺點是靈活性不足。基於OP的建模由於更加偏向底層數據處理,開發人員只需要關心對當前張量數據進行什麼樣的數據轉換操作而無需刻意留意所在網絡的哪一層,通過添加新算子來驗證自己的研究成果或者復現論文的結論都很方便,當然基於OP的建模方式往往會使得模型冗長,如果不做詳細解釋想做二次開發或修改會比較困難。

SameDiff另一個比較重要的功能是直接支持Tensorflow模型的導入。在Deeplearning4j之前的版本中,直接支持的是導入Keras的模型,而導入TF的模型都需要間接通過Keras,因此並不方便。SameDiff支持導入大部分的TF算子,具體可以參考官方的測試demo工程(https://github.com/deeplearning4j/TFOpTests)。這篇文章暫且先不詳細介紹SameDiff支持的所有算子,在後續的文章中會陸續介紹常見算子的使用。本文先從構建一個線性迴歸模型開始介紹基於OP建模的整體思路,包括計算流圖的構建、訓練數據準備、參數訓練、指標驗證和數據預測這幾個方面。

首先需要說明的是,我使用的是最新發布的1.0.0-beta5的版本。相對於上一個1.0.0-beta4的版本,該版本修復了一些bug,因此建議大家使用最新的版本。這裏我們使用Maven管理相關依賴,下面是需要導入的必要依賴清單:

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<nd4j.version>1.0.0-beta5</nd4j.version>
	<dl4j.version>1.0.0-beta5</dl4j.version>
</properties>

<dependencies>
    <dependency>
	    <groupId>org.nd4j</groupId>
	    <artifactId>nd4j-native-platform</artifactId>
	    <version>${nd4j.version}</version>
    </dependency>
    <dependency>
	    <groupId>org.deeplearning4j</groupId>
		<artifactId>deeplearning4j-core</artifactId>
		<version>${dl4j.version}</version>
	</dependency>
    <!-- 省去部分依賴 -->
<dependencies>
    

這些個依賴其實在之前的文章中也多次提及,這裏不再贅述了,如果有不清楚的同學可以翻一下之前的文章,不過需要注意下版本的問題。

我們可以在IDE中查看當前版本SameDiff支持的一些算子。在之前文章中我們介紹的一些神經網絡結構,比如CNN、RNN在這裏都可以通過SDCNN、SDRNN中的方法調用來實現。SameDiff和TF一樣是靜態圖計算模式,需要先聲明所有的數據節點/變量,構建完整個流圖後再以數據驅動訓練參數。SameDiff和TF類似將數據變量分爲以下幾類:

  • VARIABLE:可訓練數據變量,模型參數
  • CONSTANT:數據常量
  • PLACEHOLDER:臨時數據變量,一般用作不定規模的輸入和輸出數據
  • ARRAY:臨時數據變量,用來存儲OP計算後的臨時結果

以上數據類型的具體描述可以參考官網鏈接:https://deeplearning4j.org/docs/latest/samediff-variables

1. 聲明計算流圖上下文

SameDiff sd = SameDiff.create();


我們可以通過調用SameDiff的靜態工廠方法來聲明一個上下文實例。一般我們不帶任何參數來創建這個實例,即不含有任何變量的空實例。

2. 聲明數據變量和計算圖

        //聲明placeholder變量
        SDVariable input = sd.placeHolder("input", DataType.FLOAT, -1, NUM_FEATURE);  // ? x 4
        SDVariable label = sd.placeHolder("label", DataType.FLOAT, -1, 1); //? x 1
        //聲明可訓練的variable參數
        SDVariable weights = sd.var("weights", new XavierInitScheme('c', NUM_FEATURE, NUM_OUTPUT), DataType.FLOAT, NUM_FEATURE, NUM_OUTPUT);
        SDVariable bias = sd.var("bias", DataType.FLOAT, 1);
        //op算子構建計算圖
        SDVariable predicted = input.mmul(weights).add("predicted", bias);   //y = w .* x + b 
        sd.loss.meanSquaredError("mseloss", label, predicted);


可以看到我們先聲明瞭計算圖的輸入和輸出,即模型的特徵和標註,對於線性迴歸模型來說需要訓練的模型參數是各個特徵的權重以及偏置項,因此weights和bias需要設置成VARIABLE類型。我們通過mmul和add兩個基礎的算子構建整個計算流程,由此得到的輸出和原始標註信息做均方誤差作爲loss。SDLoss類中集成了主流的損失函數,這裏我們通過sd.loss.meanSquaredError直接調用。到此計算流圖的構建就完成了。

3. 建模參數定義

        double learningRate = 1e-2;
        TrainingConfig config = new TrainingConfig.Builder()                             
            .updater(new Sgd(learningRate))        
            .dataSetFeatureMapping("input")         
            .dataSetLabelMapping("label")           
            .build();
        sd.setTrainingConfig(config);
        sd.setListeners(new ScoreListener(1));


這部分主要是通過TrainingConfig工具類來配置優化算法、學習率等超參數。

4. 構建數據集

        //構建訓練數據集和驗證數據集
        INDArray indFeature = Nd4j.rand(NUM_SAMPLES, NUM_FEATURE);
        INDArray indLabel = Nd4j.rand(NUM_SAMPLES, NUM_OUTPUT);
        DataSet ds = new DataSet(indFeature, indLabel);
        SplitTestAndTrain train_test = ds.splitTestAndTrain(0.7);
        DataSet dsTrain = train_test.getTrain();
        DataSet dsTest = train_test.getTest();
        DataSetIterator trainIter = new ListDataSetIterator<DataSet>(Lists.newArrayList(dsTrain), BATCHES);
        DataSetIterator testIter = new ListDataSetIterator<DataSet>(Lists.newArrayList(dsTest), BATCHES);


在數據構建這個部分,我們隨機生成了1024條數據集並且按照7:3的比例切分成了訓練數據集和驗證集,接着我們對這些數據封裝成迭代器的形式,這和之前我們基於Layer建模的數據結構是相同的。

5. 訓練模型

        sd.fit(trainIter, NUM_EPOCHES);


訓練模型的部分只需要將剛纔構建的訓練集和需要訓練的輪次作爲參數傳入fit接口即可

6. 驗證模型

        String outputVariable = predicted.getVarName();
        assert(outputVariable.equals("predicted"));
        RegressionEvaluation evaluation = new RegressionEvaluation();
        sd.evaluate(testIter, outputVariable, evaluation);
        System.out.println(evaluation.stats());


我們的數據是隨機生成的(服從正態分佈),用線性模型去擬合肯定不會收斂得很好。在這裏我們只是爲了說明驗證的方法,所以具體指標數據並不需要太過關注。

7. 單條數據預測

        INDArray indSingle = indFeature.getRow(0L);
        INDArray indSingleLabel = indLabel.getRow(0L);
        INDArray result = sd.output(new HashMap<String,INDArray>(){{put("input",indSingle);}}, "predicted").getOrDefault("predicted", null);
        assert( result != null );
        System.out.println(String.format("Predict: %s, Label: %s", result, indSingleLabel));


我們從之前隨機生成的數據集中抽取第一條作爲樣例數據,output會進行前向傳播計算,以此得到預測值。這裏我們和真實的輸出做了比較。下面是我們打出的一些日誌供大家參考。

熟悉TF編程的開發人員對以上建模過程相信會有種似曾相識的感覺。包括一些PLACEHOLDER、VARIABLE類型變量的聲明,基本張量計算算子的調用等和TF的編程模式非常相似。其實SameDiff支持導入TF模型的原因就是在於這些常用算子的兼容。目前TF正在推2.0的版本,很大方向是推廣基於Layer的編程API,也就是Keras的接口。但當前各種開源代碼中,基於OP實現模型的還是佔大多數,因此熟悉這種編程模式還是很有必要的。在後面的文章中我會陸續介紹SameDiff常用算子以及TF模型導入和再訓練的相關內容。

如果開發人員需要了解計算圖的詳情,比如搭建完流圖後涉及的變量類型有哪些,涉及到的算子有哪些,那麼通過調用SameDiff的summary接口並通過控制檯打印的內容來查看相關信息。

        //模型描述
        System.out.println(sd.summary());

從summary的結果中可以看到,一個涉及8個變量,3個算子/方法。明細在下方有所展示。由於我們設置的batchSize是4,所以input變量是一個4*4的矩陣。可訓練參數weights是一個含有4個參數的向量。涉及到的算子在我們構建計算圖的邏輯中提到過,即mmul、add還有我們調用的損失函數mse。

爲了比較直觀地解釋計算流圖的結構,我們可以參考下面這張圖。

在訓練結束後,可能我們需要查看特徵權重,也就是weights和bias的值,我們可以通過以下方式查看。

        //模型參數獲取
        INDArray trainedWeights = sd.getVariable("weights").getArr();
        INDArray trainedBias = sd.getVariable("bias").getArr();
        System.out.println(String.format("Weights: %s, Bias: %s", trainedWeights, trainedBias));

這裏對整篇文章做下小結。SameDiff是Deeplearning4j推出的自動微分計算框架,目前集成在ND4j的項目中。目Deeplearning4j的一些新模型結構的實現,比如Attention機制就是基於SameDiff來實現。基於SameDiff提供的OP進行算法建模是偏重底層的建模方式,雖然本質上和基於Layer的方式是一致的,但在實現方式和編程思維上還是有比較多的差異。我們以一個比較常見的線性迴歸模型闡述了基於OP建模的整體思路。雖然張量的轉換不是很複雜,但是展現了基本的建模結構,可作爲大家使SameDiff的入門參考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章