創建NTL+語言的神經網路 | IFCM
IFC Markets - 外匯交易

創建NTL+語言的神經網路

引言

人工神經網路 - 是由數學模型, 及其機器硬體和軟體的實現, 是基於生物神經網路的組合和功能 - 大腦的神經細胞網路.

當前神經網路應用廣泛, 例如識別,分類,聯合存儲,規律性的確定,預測等等.

神經網路需要單獨的數學或基於數學包的附加模型, 以便於提供不同類型和配置方案的網路結構

我們試圖基於NTL+語言重新創建自己的網路,並同時嘗試物件導向的程式設計.

問題選擇

在本文中我們來測試, 基於過去的燭圖可以預測未來柱圖的確定走勢: 下降或上漲。

這涉及分類問題。我們公司的金融工具允許使用歷史資料,以此獲得專業的評估。並創建基於多層感知的網路。

因此,根據k柱的收盤價創建網路而預測隨後燭圖的狀態. 如果其收盤價高於前一柱, 那麼取輸出值1(價格上漲)。在其他情況下,輸出值取0(價格沒有變化或下跌)。

網路建設

一般而言,多層感知具有一個輸入層,一個或多個隱藏層和一個輸出層。單個隱藏層足夠進行輸入轉為輸出. 使用更多數量的隱藏層只能導致網路學習速度明顯放緩, 而沒有顯著的學習品質.

在網路中保留1個輸入層, 一個隱藏層和一個輸出層. 在下面的圖中顯示網路的體系結構. x1 - xn - 輸入 (收盤價), wi,j - 自節點i至j的邊緣權重; y1 - ym 隱層神經元, o1 - ok 網路輸出.


網路也使用輸入偏移(偏移輸入). 使用這些輸入可以確保我們的網路沿x軸移動啟動的函數, 從而不僅改變啟動函數的斜率, 還可以保證線性偏移.

1. 啟動函數表 = 1
2. 啟動函數表 при =2 和 = 1
3. 啟動函數表, 偏移為 =2, = 1 和 = 1

每個網路節點的值將按照下面的公式計算:
, где f(x) - 啟動函數, n - 前一層的節點數.

啟動函數

啟動函數來計算經過加法器之後獲得輸出信號。 人工神經元通常作為一個非線性函數的參數。最常用的啟動函數:

費米函數(S型指數):


合理的S型:


雙曲正切


確定每個節點的輸出需要選擇合理的S型, 因其計算需要較少時間.

網路學習的過程

為了學習, 我們的神經網路實現了誤差的反向傳輸. 該方法是利用了多層感知達到誤差最小化的重複演算法. 演算法的主要思想是: 在神經網路輸出計算後, 計算每個節點和邊的誤差W的偏差, 並且錯誤的計算方法是由輸出端至輸入端. 之後按照誤差值校正權重W . 該演算法對啟動函數的唯一的要求是: 被微分. S型和雙曲正切滿足這一要求.

因此, 在學習的過程中, 我們需要做到以下幾點:

  1. 初始化所有邊的權重隨機值.
  2. 對於所有的輸出, 神經網路計算統計修正
    , 其中 oj - 神經網路計算的輸出, tj - 實際值
  3. 除了最後一個節點, 其他都由下面的公式計算
    , wj,k 節點輸出的邊的權重用於計算修正 節點計算的誤差位於兩個系那個林的輸出層.
  4. 對於每個神經網路的邊計算修正:
    , 其中oi 自邊的輸出的節點計算輸出 被計算的節點輸出, 用於計算修正, 而被計算的節點修正, 其中包括確定的邊.
  5. 校正所有邊的權重值:
  6. 重複步驟2 - 5 , 針對所有學習的例子或暫時未達到品質準則的.

準備輸入資料

對於實現神經網路的學習需要準備輸入資料, 有品質的輸入值可以很好的影響網路的工作及其參數w的穩定速度, 也就是學習的過程.

所有的輸入向量被正常化, 以便起分量處於區間[0;1] 或 [-1;1]. 正常化使得所有輸入向量在網路學習的過程中是平等的, 從而保障學習的修正.

我們來正常化輸入向量 - 使其分量處於[0;1], 對此需要使用下列公式:

作為向量的分量, 將進入柱體的收盤價, 並且作為輸入移動至[n+k;n+1]柱體的收盤價, 其中k是輸入向量的大小. 希望值的確定基於n柱體. 其值大小的確定將遵循: 如果n柱的收盤價大於n+1柱的收盤價, 則取希望值為1, 如果低於或等於, 那麼取0;

在圖表中標出了黃色的柱體, 其參與組成每組資料(#1, #2), 橙色的是用於確定希望值.

在網路學習的過程中, 也會影響資料的次序也會影響學習的過程. 如果輸入向量均勻提供1和0,那麼學習的過程更穩定.

同樣形成了單獨的資料組, 用於評估網路工作的有效性. 這些資料網路不會用於學習, 僅用於按照最小二次方的方法計算錯誤. 在測試組資料添加10%的輸入例子. 其結果是90%例子將用學習, 10%用於評估.

最小二次方計算錯誤的功能有下列方式:
,
где - 網路輸出信號和 - 輸出信號的需要值.

查看準備為神經網路輸入向量的腳本代碼

分析DataSet類 - 資料組. 該類包括:

  • 輸入值向量陣列 - input
  • 生出輸出值- output
  • Normalize 方法- 標準化(正常化)資料
  • OutputDefine方法- 確定價格的實際值
  • AddData方法- 在輸入向量的記錄值和變數的實際值
  • To_file 方法 - 單矩陣的資料組, 針對下一條目

class DataSet { array <double> input(inputVectorSize); double output; //Normalization void Normalize() { double min = input[0]; double max = input[0]; //Finding minimum and maximum values for(uint i=0;i<input.length();i++) { if(input[i]>max) max=input[i]; if(input[i]<min) min=input[i]; } //Normalizing data for(uint i=0;i<input.length();i++) { //If min==max, setting all values to 0 if(max-min<0.000005) { input[i] = 0; } else { input[i]=(input[i]-min)/(max-min); } } } //Calculating the output void OutputDefine() { //If the price goes up, output is set to 1. if(input[inputVectorSize-1]<output) { output=1; } //If the price goes down or does not change, output is set to 0 else { output=0; } } //Splitting the rawInput array into two parts: input array and output value void AddData(array <double> rawInput) { //If the sizes of the arays don't match, we show a message. if(rawInput.length()!=uint(inputVectorSize+1)) { System.Print("Wrong input array size"); } //Writing to the 'input' array and 'output' variable for(int i=0;i<inputVectorSize;i++) { input[i]=rawInput[i]; } output=rawInput[inputVectorSize]; } // Merging the 'input' array and 'output' variable into one array array<double> To_file() { array<double> temp(inputVectorSize+1); for(int i=0;i<inputVectorSize;i++) { temp[i]=input[i]; } temp[inputVectorSize]=output; return temp; } }

我們來看Run()函數的程式碼, 其實現下列步驟:

  • 載入歷史資料. 包括圖表的字元和時期.
  • 截資料組為輸入向量+ 輸出值的長度
  • 形成輸入向量的資料組. 標準化這些向量. 確定希望值是1或0.
  • 形成輸入向量, 相對對於0和1
  • 資料組主要部分的記錄和用於測試的其餘部分的記錄, 並通過最小二次方的方法進行評估.
int Run() { // The array that holds close prices of all loaded bars array <double> data (Chart.Bars-1); // Writing data to the array 'data' for(int i=1; i < Chart.Bars;i++) { data[i-1]=Close[i]; } // Truncating excessive data that can't form the whole set data.resize((data.length/(inputVectorSize+1))*(inputVectorSize+1)); int num=0; // The array is for holding all input vectors array<DataSet> sets(data.length/(inputVectorSize+1)); array <double> temp(inputVectorSize+1); // The array is for holding input vectors in the correct order array <DataSet> alternation; // Forming elements in the 'sets' array: normalizing each vector and calculating output (0 or 1) for(int i=data.length-1;i>=0;i-=(inputVectorSize+1)) { for(int j=0;j<inputVectorSize+1;j++) { temp[j]=data[i-j]; } sets[num].AddData(temp); sets[num].OutputDefine(); sets[num].Normalize(); num++; } // Forming the array 'alternation', where all the sets are sorted, so that they go 1,0,1,0 etc. uint len = sets.length; for(uint i=0;i<len;i++) { for(uint j=0;j<sets.length;j++) { if(sets[j].output==0 && state==1) { alternation.insertLast(sets[j]); sets.removeAt(j); state = 0; break; } if(sets[j].output==1 && state==0) { alternation.insertLast(sets[j]); sets.removeAt(j); state = 1; break; } } } // The file for writing data for learning file f; f.Open("data.txt",fmWrite|fmText); // Calculating the number of sets with data for learning and testing data uint datasets = int(alternation.length()*0.9); uint testsets = alternation.length() - datasets; System.Print("datasets SETS = "+datasets); // Writing data for learning to the file for(uint i=0;i<datasets;i++) { for(int j=0;j<inputVectorSize+1;j++) { f.WriteDouble(alternation[i].To_file()[j]); } } f.Close(); System.Print("testsets SETS = "+testsets); // Data for testing // Writing to the file file t; t.Open("test.txt",fmWrite|fmText); for(uint i=0;i<testsets;i++) { for(int j=0;j<inputVectorSize+1;j++) { t.WriteDouble(alternation[i+datasets].To_file()[j]); } } t.Close(); return(0); }

在本文中, 您還需要聲明一個全域變數int state = 0, 需要存在輪換的輸入向量.

創建神經網路的種類

對於我們的網路需要:

  • 聯合輸入層和隱藏層的類layer
  • 聯合隱藏層和輸出層的類layer
  • 連接各層的類 net

創建類layer-層

我們需要包含下列屬性和方法的類:

  • 保存神經網路的輸入'input'陣列
  • 保存神經網路的輸出'output'陣列
  • 保存神經網路的修正'delta'陣列
  • 所有邊的權重'weights' 的雙層陣列
  • 'LoadInputs'方法賦值輸入層
  • 'LoadWeights'方法載入層的權重
  • 'SaveWeights'方法保存層的權重至硬碟中
  • 選出使用陣列所需要的記憶體
  • 'RandomizeWeights'方法, 填寫權重隨機值
  • 'OutputCalculation'方法,計算輸出值
  • 'CalculatingDeltaLast' 和 CalculatingDeltaPrevious方法, 計算修正值
  • 'WeightsCorrection'方法, 修正權重值
  • 補充(診斷)方法, 輸出資訊至螢幕.:
    • PrintInputs() - 列印輸入層
    • PrintOutputs() - 列印輸出層 ('OutputCalculation'幫助下, 在計算輸出之後打開)
    • PrintDelta() - 列印層的修正(在'CalculatingDeltaLast' 或 'CalculatingDeltaPrevious'幫助下, 在計算修正之後打開)
    • PrintWeights() - 列印層的權重w

    所得類放置在單獨的檔中. 此外, 在該檔中將存儲的網路配置: 輸入層, 隱藏層和輸出層的節點數量. 這些變數被取出使得同樣用於準備輸入資料的腳本.

    extern int inputVectorSize = 8; // input vector extern int L1_Innersize = 8; // number of knots in the hidden layer without bias extern int L2_Innersize= 1; // number of knots in the output layer without bias extern double nu = 0.1; //learning rate in the backpropagation method class layer { // input values array<double> input; // output values array<double> output; // output array size int outputSize=0; // weights array<array <double>> weights; // amendments array<double> delta; // default constructor layer() { } // loading input values from the 'inp' array. bool LoadInputs(array <double> inp) { if(inp.length()+1 != input.length()) { System.Print("Loading is not possible. Array sizes do not match"); return false; } for(uint i=0;i<inp.length();i++) { input[i]=inp[i]; } return true; } // loading weights from a specifies file bool LoadWeights(string filename) { file f; if(!f.Open(filename,fmRead|fmText)) { return false; } for(uint i=0;i<weights.length();i++) { for(uint j=0;j<weights[0].length();j++) { weights[i][j]=f.ReadDouble(); } } f.Close(); return true; } // loading weights to a specified file bool SaveWeights(file f) { for(uint i=0;i<weights.length();i++) { for(uint j=0;j<weights[0].length();j++) { if(!f.WriteDouble(weights[i][j])) { f.Close(); return false; } } } return true; } // constructor // inp - array of values // size - size of output array layer(array<double> inp, int size) { input = inp; input.insertLast(1); // Bias outputSize = size; //weigths array array<array<double>> temp_weights(size, array<double>(inp.length()+1)); array<double> temp_output(size); output = temp_output; weights = temp_weights; array<double> delta_temp(size); delta = delta_temp; } //randomizing weights void RandomizeWeights() { for(uint i=0;i<weights.length();i++) { for(uint j=0;j<weights[0].length;j++) { weights[i][j]=(-0.5+double(Math.Rand())/32767)*0.1; } } } // printing weights void PrintWeights() { string line; for(uint i=0;i<weights.length();i++) { line = "weights"; for(uint j=0;j<weights[0].length;j++) { line += " ["+i+"]["+j+"]="+weights[i][j]; } System.Print(line); } } // printing input values void PrintInputs() { for(uint i=0;i<input.length;i++) { System.Print("inputs["+i+"]="+input[i]); } } // print output values void PrintOutputs() { for(uint i=0;i<output.length;i++) { System.Print("outputs["+i+"]="+output[i]); } } // printing delta values void PrintDelta() { for(uint i=0;i<delta.length();i++) { System.Print("delta ["+i+"]="+delta[i]); } } // output calculation void OutputCalculation() { // temporary array to store output values array<double> a(outputSize,0); for(int k=0;k<outputSize;k++) { for(uint i=0;i<input.length();i++) { a[k]+=weights[k][i]*input[i]; } a[k]=Activation(a[k]); } output = a; } //Changing weights of the last layer void CalculatingDeltaLast(array <double> realValues) { //System.Print("delta.length()="+delta.length); if(realValues.length()!=delta.length()) { System.Print("不同大小的陣列的差異"); return; } for(uint i=0;i<realValues.length();i++) { delta[i]=-output[i]*(1-output[i])*(realValues[i] - output[i]); } } //Changing weight for any layers except for the last. void CalculatingDeltaPrevious(layer &inout LayerLast) { for(uint j=0;j<LayerLast.input.length()-1;j++) { double sum = 0; for(uint k=0;k<LayerLast.output.length();k++) { sum += LayerLast.delta[k]*LayerLast.weights[k][j]; } delta[j]=output[j]*(1-output[j])*sum; } } //Changed weights void WeightsCorrection() { for(uint i=0;i<weights.length();i++) { for(uint j=0;j<weights[0].length();j++) { weights[i][j] = weights[i][j] - nu*delta[i]*input[j]; } } } }

    我們還需要輔助的功能: 標準化輸入向量和啟動功能

    void Normalize(array<double> &inout input) { double min = input[0]; double max = input[0]; //finding minimum and maximun for(uint i=0;i<input.length();i++) { if(input[i]>max) max=input[i]; if(input[i]<min) min=input[i]; } for(uint i=0;i<input.length();i++) { if(max-min<0.000005) { input[i] = 0.0; } else { input[i]=(input[i]-min)/(max-min); } } } // Activation function double Activation(double x) { return x/(Math.Abs(x)+1); }

    創建網路的類net

    我們還需要這樣的類, 即可以統一層和唯一的網路, 因此這樣的類需要包括:

    • 'Calculate'方法, 連接層來計算網路的輸出. 該方法將用於網路的學習.
    • 'CalculateAndLearn'方法, 計算輸出, 修正和錯誤. 計算輸出您需要使用前一個方法, 而對於修正和錯誤您需要引用每個層的相應的方法
    • 'SaveNetwork '方法, 保存網路的參數.
    • 'LoadNetwork'方法, 載入網路的參數.
    class net { array <double> x(inputVectorSize); array <double> y(L1_Innersize); layer L1(x,L1_Innersize); array<double> L1_outputs(L1_Innersize); layer L2(y,L2_Innersize); //Calculating output using current weights array<double> Calculate(array <double> data, bool randomWeights) { if(data.length()!=x.length()) { System.Print("Array sizes do not match"); } x = data; Normalize(x); L1.LoadInputs(x); if(randomWeights) { L1.RandomizeWeights(); L2.RandomizeWeights(); } L1.OutputCalculation(); L2.LoadInputs(L1.output); L2.OutputCalculation(); return L2.output; } //Calculating output and changing weights void CalculateAndLearn(array <double> data, array <double> realValues, bool randomWeights) { Calculate(data, randomWeights); L2.CalculatingDeltaLast(realValues); L1.CalculatingDeltaPrevious(L2); L2.WeightsCorrection(); L1.WeightsCorrection(); } //Saving the weights of the whole network bool SaveNetwork(string filename) { file f; if(!f.Open(filename,fmWrite|fmRead|fmText)) { f.Open(filename,fmWrite|fmText); } L1.SaveWeights(f); L2.SaveWeights(f); f.Close(); return true; } //Loading all weights bool LoadNetwork(string filename) { file fn; if(fn.Open(filename,fmRead|fmText)==false) { return false; } for(uint i=0;i<L1.weights.length();i++) { for(uint j=0;j<L1.weights[0].length();j++) { L1.weights[i][j]=fn.ReadDouble(); } } for(uint i=0;i<L2.weights.length();i++) { for(uint j=0;j<L2.weights[0].length();j++) { L2.weights[i][j]=fn.ReadDouble(); } } return true; } }

    檢查網路的工作

    在本節中,我們以一個基本的例子來檢查網路的工作, 在其幫助下我們可以確定輸出向量的正確分類. 對此將使用2個輸入層和隱藏層, 1個輸出層的神經網路. 參數nu 設置為1. 輸入資料的集如下:
    輸入 1,2 和希望值 1
    輸入 2,1 和希望值 0
    在文找中將是如下形式::

    1.00000000 2.00000000 1.00000000 2.00000000 1.00000000 0.00000000

    輸入不同數量的學習的例子, 可以看到我們的網路學習的過程.


    圖中紅線對應1的學習例子. 藍線 - 對應0. 根據各自橫軸截取學習的例子, 根據縱軸計算學習例子的網路值. 可以看出, 如果例子的數量少(25及其以下)網路不能差別不同的輸入向量, 如果數量多- 很明確的分為兩類.

    評估網路

    為了評估學習的效果, 我們使用最小二次方的方法計算錯誤. 對此要創建具有以下代碼的實用程式並運行它. 在此添加2個文件: "test.txt" - 計算錯誤時用到的資料和希望值, "NT.txt" - 已計算參數的文件.

    該腳本完成下列步驟:

    • 創建net類的對象NT
    • 校對參數w 並載入至NT
    • 創建陣列的網路輸出和輸入
    • 校對輸入值並置於陣列'x'中, 校對輸入並置於'reals'
    • 計算網路的輸出, 提供給數位'x'
    • 計算錯誤error, 所有計算值和實際值差異的平方和
    • 當臨近檔結尾, 輸出錯誤值並退出腳本
    #include "Libraries\NN.ntl" int Run() { net NT; file f; int counter = 0; // Loading weights NT.LoadNetwork("NT.txt"); double error=0; // Declaring an array for storing culculated outputs array <double> CalculatedOutput (L2_Innersize); // Opening a file with test data if(f.Open("test.txt",fmRead|fmText)==false) { System.Print("Input data not found"); return -1; } // Array holds input values array <double> x(inputVectorSize); // Actual output array <double> reals(L2_Innersize); while(true) { for(int i=0;i<inputVectorSize;i++) { x[i]=f.ReadDouble(); if(f.IsEOF()) { System.Print("counter="+counter+"error="+0.5*error+"error/counter="+(0.5*error/counter)); return -1; } } //forming real examples for(int j=0;j<L2_Innersize;j++) { reals[j]=f.ReadDouble(); System.Print("Real exit = "+reals[j]); } CalculatedOutput = NT.Calculate(x,false); System.Print("Calculated exit = "+CalculatedOutput[0]); //calculating error for(uint i=0;i<reals.length();i++) { error+=(reals[i]-CalculatedOutput[i])*(reals[i]-CalculatedOutput[i]); } counter++; } return(0); }

    創建指標

    我們需要變數用於保存被計算的網路輸出, 如果輸出值為1 , 那麼1個變數足夠, 但是網路常有多個輸出, 因此需要一個值的陣列我們將其放置在行array CalculatedOutput (L2_Innersize);. 在初始函數中, 我們定義了指標的參數, 類型, 相關的屬性值(兩個記憶體值). 同樣我們還需要重置網路的參數, 即載入所有的在學習中的權重參數. 這通過對象NT的 LoadNetwork(string s)方法, 及一個參數- 包含權重參數的檔案名. 在函數Draw中我們組成了輸入向量x , 對應指數[pos+inputVectorSize-1; pos]收盤價, 其中pos - 計算值的柱體號, 而inputVectorSize 輸入向量的長度. 在結尾調取物件NT的函數Calculate , 返回陣列的值(如果網路有幾個輸出值), 因為我們的輸出只有一個 - 使用指數為0的陣列的單元.

    #set_indicator_separate #include "Libraries\NN.ntl" double ExtMapBuffer1[]; double ExtMapBuffer2[]; int ExtCountedBars=0; net NT; array <double> CalculatedOutput (L2_Innersize); int Initialize() { Indicator.SetIndexCount(2); Indicator.SetIndexBuffer(0,ExtMapBuffer1); Indicator.SetIndexStyle(0,2,0,3,0xFF0000); Indicator.SetIndexBuffer(1,ExtMapBuffer2); Indicator.SetIndexStyle(1,2,0,3,0xFF0000); NT.LoadNetwork("NT.txt"); return(0); } int Run() { ExtCountedBars=Indicator.Calculated; if (ExtCountedBars<0) { System.Print("Error"); return(-1); } if (ExtCountedBars>0) ExtCountedBars--; Draw(); return(0); } void Draw() { int pos=Chart.Bars-ExtCountedBars-1; array <double> x(inputVectorSize); while(pos>=0) { ExtMapBuffer1[pos]=0; // forming input vector for(int i=pos+inputVectorSize-1; i>=pos; i--) { x[i-pos]=Close[i]; } ExtMapBuffer2[pos]=NT.Calculate(x,false)[0]; pos--; } }

    顯示的長條圖反映下一個柱圖的預期. 值從0.5-1說明下一個柱圖很有可能是下降的. 值從0至0.5則說明下一個柱圖很有可能是上漲的. 0.5對應的是不確定. 即下一個柱圖可能上漲, 也可能下降.

    概要

    神經網路是技術分析的強大工具. 在本文中展示了通過NTL+利用物件導向的語言來創建神經網路的過程. 使用物件導向的方法使得簡化代碼, 並更容易用在未來的腳本中. 在實踐中, 我們創建了類 layer 描述神經網路的層和類net描述整體的網路. 類net我們用於確定錯誤數量和計算權重的指標. 此外還舉例展示了如何利用學習網路.

Close support
Call to Skype Call to QQ Call Back