MQL dili kullanarak sıfırdan bir Derin Sinir Ağı programlama
Giriş
Makine öğrenimi giderek daha popüler hale geliyor ve birçoğunuz muhtemelen Derin Öğrenmeyi duymuşsunuzdur. MQL dilinde Derin Öğrenmenin nasıl uygulanacağını bilmek gerçekten ilgi çekicidir. Aktivasyon fonksiyonlarına sahip yapay nöronların basit uygulamalarını gördüm, ancak henüz gerçek bir Derin Sinir Ağı uygulayan hiçbir şeye rastlamadım. Bu makalede, gizli katmanlar için hiperbolik tanjant fonksiyonu ve çıktı katmanı için Softmax fonksiyonu gibi farklı aktivasyon fonksiyonlarıyla MQL dilinde uygulanmış bir Derin Sinir Ağını sizlere tanıtacağım. İlk adımdan son adıma kadar yavaş yavaş sinir ağını öğreneceğiz ve birlikte bir Derin Sinir Ağı oluşturacağız.
1. Yapay nöron oluşturma
Sinir ağının temel birimi olan nöronla başlayalım. Burada, Derin Sinir Ağımızda kullanacağımız nöron tipinin farklı kısımlarına odaklanacağım. Aslını söylemek gerekirse, nöron tipleri arasındaki en büyük fark genellikle aktivasyon fonksiyonudur.
1.1. Bir nöronun kısımları
İnsan beynindeki nörondan modellenen yapay nöron, sadece matematiksel hesaplamalar barındırır. Nöronlarımızda olduğu gibi, yeterli uyaranla karşılaştığında tetiklenir. Nöron, girdiyi, girdiyi güçlendiren veya zayıflatan bir dizi katsayı veya ağırlıkla birleştirir. Bu, algoritmanın öğrenmeye çalıştığı göreve ilişkin girdi verilerine önem verilmesine olanak sağlar. Aşağıdaki görüntü, bir nöronun farklı kısımlarının nasıl çalıştığını göstermektedir:
1.1.1. Girdiler
Ağ tarafından değerlendirilecek olan girdi ya çevreden gelen harici bir tetikleyicidir ya da diğer yapay nöronların çıktılarından gelir. Nöron için “besin” görevi görür ve içinden geçerek nörona verdiğimiz eğitim sayesinde yorumlayabileceğimiz bir çıktı haline gelir. Ayrık veya reel sayılar olabilirler.
1.1.2. Ağırlıklar
Ağırlıklar, kendilerine karşılık gelen girdilerle çarpılan, değerlerini artıran veya azaltan, böylece nöronun içine giren girdiye daha fazla veya daha az önem veren ve dolayısıyla nörondan çıkan çıktıyı etkileyen katsayılardır. Sinir ağı eğitimi algoritmalarının amacı, problemin çözülmesi için olası "en iyi" ağırlık değerleri kümesini belirlemektir.
1.1.3. Net girdi fonksiyonu
Nöronun bu kısmında, girdiler ve ağırlıklar, tek bir değere birleştirilir. Bu değer, her bir girdinin ağırlığı ile çarpımının sonuçlarının toplamı olarak hesaplar. Daha sonra bu değer, nöron girdilerinin sinir ağı çıktısı üzerindeki etkisinin ölçülerini veren aktivasyon fonksiyonundan geçirilir.
1.1.4. Aktivasyon fonksiyonu
Aktivasyon fonksiyonu çıktıya yol açar. Farklı türde aktivasyon fonksiyonları vardır (Sigmoid, Tan-h, Softmax, ReLU ve diğerleri). Nöronun aktive edilip edilmeyeceğine karar verir. Bu makalede iki tip aktivasyon fonksiyonu ile çalışacağız: Tan-h ve Softmax.
1.1.5. Çıktı
Nöronun son kısmı çıktıdır. Çıktı başka bir nöronun girdisine veya dış ortama aktarılabilir. Bu değer, kullanılan aktivasyon fonksiyonuna bağlı olarak ayrık veya reel sayı olabilir.
2. Sinir ağı oluşturma
Sinir ağı, beyin gibi biyolojik sinir sistemlerinin bilgi işleme ilkelerine benzer bir bilgi işleme modelidir. Her katmanın diğerine bağlı olduğu yapay nöron katmanlarından oluşur. Bu nedenle, bir önceki katman, bir sonraki katman için bir girdi görevi görür ve çıktı katmanına kadar bu şekilde devam eder. Sinir ağının amacı, denetimsiz öğrenme yoluyla kümeleme, denetimli öğrenme yoluyla sınıflandırma veya regresyon olabilir. Bu makalede, BUY (al), SELL (sat) veya HOLD (tut) olmak üzere üç duruma sınıflandırma yeteneğine odaklanacağız. Aşağıda tek gizli katmana sahip bir sinir ağı verilmiştir:
3. Sinir ağından Derin Sinir Ağına ölçeklendirme
Bir Derin Sinir Ağını daha yaygın olan tek-gizli-katmanlı sinir ağlarından ayıran şey, derinliğini oluşturan katmanların sayısıdır. Ağda üçten fazla katman varsa (girdi ve çıktı dahil), bu "derin" öğrenme olarak nitelendirilir. Yani "derin", birden fazla gizli katmana sahip olmak anlamına gelen iyi tanımlanmış bir teknik terimdir. Sinir ağında ne kadar derine inerseniz, nöronlarınız önceki katmandaki özellikleri toplayıp yeniden birleştirirken o kadar karmaşık özellikler tanıyabilir. Böylece, derin öğrenme ağları, doğrusal olmayan fonksiyonlardan geçen milyarlarca parametreye sahip çok büyük çok boyutlu veri kümelerini işleyebilir. Aşağıdaki görüntü, üç gizli katmana sahip bir Derin Sinir Ağını göstermektedir:
3.1. Derin Sinir Ağı sınıfı
Şimdi sinir ağımızı oluşturmak için kullanacağımız sınıfa bakalım. Derin Sinir Ağı, DeepNeuralNetwork adlı program tanımlı bir sınıfta kapsüllenir. Ana metot, bir 3-4-5-3 tam bağlantılı ileri beslemeli sinir ağı oluşturur. Bu makalede daha sonra Derin Sinir Ağını eğitirken, ağımızı beslemek için bazı girdi örnekleri göstereceğim, ancak şimdi ağı oluşturmaya odaklanacağız. Ağ, iki gizli katmana sahip şekilde gömülü kod halindedir. Üç veya daha fazla katmana sahip sinir ağları çok nadirdir, ancak daha fazla katmana sahip bir ağ oluşturmak istiyorsanız, bu makalede sunulan yapıyı kullanarak bunu kolayca yapabilirsiniz. Girdiden A katmanına kadar olan ağırlıklar iaWeights matrisinde, A katmanından B katmanına kadar olan ağırlıklar abWeights matrisinde ve B katmanından çıktıya kadar olan ağırlıklar boWeights matrisinde depolanır. Çok boyutlu bir dizi yalnızca ilk boyutta statik veya dinamik olabileceğinden (diğer tüm boyutlar statiktir), matrisin boyutu "#define" ifadesi kullanılarak bit sabit değişken olarak bildirilir. Yer kazanmak adına en üst düzey sistem ad alanına referansta bulunan dışında tüm ifadeleri kaldırdım. Kaynak kodunun tamamını makaleye ekli dosyalarda bulabilirsiniz.
Program yapısı:
#define SIZEI 4 #define SIZEA 5 #define SIZEB 3 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class DeepNeuralNetwork { private: int numInput; int numHiddenA; int numHiddenB; int numOutput; double inputs[]; double iaWeights[][SIZEI]; double abWeights[][SIZEA]; double boWeights[][SIZEB]; double aBiases[]; double bBiases[]; double oBiases[]; double aOutputs[]; double bOutputs[]; double outputs[]; public: DeepNeuralNetwork(int _numInput,int _numHiddenA,int _numHiddenB,int _numOutput) {...} void SetWeights(double &weights[]) {...} void ComputeOutputs(double &xValues[],double &yValues[]) {...} double HyperTanFunction(double x) {...} void Softmax(double &oSums[],double &_softOut[]) {...} }; //+------------------------------------------------------------------+
İki gizli katman ve tek çıktı katmanının her biri, sırasıyla aBiases, bBiases ve oBiases olarak adlandırılan ilişkili bias değerleri dizisine sahiptir. Gizli katmanların yerel çıktıları, aOutputs ve bOutputs adlı sınıf kapsamı dizilerinde depolanır.
3.2. Derin Sinir Ağı çıktılarını hesaplama
ComputeOutputs metodu ilk olarak, ön (aktivasyon öncesi) toplamları depolamak adına geçici diziler oluşturur. Sonrasında metot, A katmanı düğümleri için girdileri ağırlıklarla çarparak ön toplamı hesaplar, bias değerlerini ekler ve aktivasyon fonksiyonunu uygular. Ardından, yeni hesaplanmış A katmanının çıktıları B katmanı için yerel girdiler olarak kullanılır ve böylece B katmanının yerel çıktıları hesaplanır. Ve son olarak nihai çıktılar hesaplanır.
void ComputeOutputs(double &xValues[],double &yValues[]) { double aSums[]; // hidden A nodes sums scratch array double bSums[]; // hidden B nodes sums scratch array double oSums[]; // output nodes sums ArrayResize(aSums,numHiddenA); ArrayFill(aSums,0,numHiddenA,0); ArrayResize(bSums,numHiddenB); ArrayFill(bSums,0,numHiddenB,0); ArrayResize(oSums,numOutput); ArrayFill(oSums,0,numOutput,0); int size=ArraySize(xValues); for(int i=0; i<size;++i) // copy x-values to inputs this.inputs[i]=xValues[i]; for(int j=0; j<numHiddenA;++j) // compute sum of (ia) weights * inputs for(int i=0; i<numInput;++i) aSums[j]+=this.inputs[i]*this.iaWeights[i][j]; // note += for(int i=0; i<numHiddenA;++i) // add biases to a sums aSums[i]+=this.aBiases[i]; for(int i=0; i<numHiddenA;++i) // apply activation this.aOutputs[i]=HyperTanFunction(aSums[i]); // hard-coded for(int j=0; j<numHiddenB;++j) // compute sum of (ab) weights * a outputs = local inputs for(int i=0; i<numHiddenA;++i) bSums[j]+=aOutputs[i]*this.abWeights[i][j]; // note += for(int i=0; i<numHiddenB;++i) // add biases to b sums bSums[i]+=this.bBiases[i]; for(int i=0; i<numHiddenB;++i) // apply activation this.bOutputs[i]=HyperTanFunction(bSums[i]); // hard-coded for(int j=0; j<numOutput;++j) // compute sum of (bo) weights * b outputs = local inputs for(int i=0; i<numHiddenB;++i) oSums[j]+=bOutputs[i]*boWeights[i][j]; for(int i=0; i<numOutput;++i) // add biases to input-to-hidden sums oSums[i]+=oBiases[i]; double softOut[]; Softmax(oSums,softOut); // softmax activation does all outputs at once for efficiency ArrayCopy(outputs,softOut); ArrayCopy(yValues,this.outputs); }Perde arkasında, sinir ağı, iki gizli katmanın çıktılarını hesaplarken hiperbolik tanjant aktivasyon fonksiyonunu (Tan-h) ve nihai çıktı değerlerini hesaplarken Softmax aktivasyon fonksiyonunu kullanır.
- Hiperbolik tanjant (Tan-h): Lojistik sigmoid gibi, Tan-h fonksiyonu da sigmoidaldir, ancak ondan farklı olarak (-1, 1) aralığındaki değerleri verir. Bu nedenle, Tan-h'e olan güçlü negatif girdiler, negatif çıktılarla sonuçlanacaktır. Ek olarak, yalnızca sıfır değerli girdiler, sıfıra yakın çıktılara yol açacaktır. Aşağıda Tan-h fonksiyonunun matematiksel formülünü ve aynı zamanda MQL kaynak kodundaki uygulamasını göstereceğim.
double HyperTanFunction(double x) { if(x<-20.0) return -1.0; // approximation is correct to 30 decimals else if(x > 20.0) return 1.0; else return MathTanh(x); //Use explicit formula for MQL4 (1-exp(-2*x))/(1+exp(-2*x)) }
- Softmax: Birden fazla sınıf olması durumunda her sınıfa ondalık olasılıklar atar. Bu ondalık olasılıkların toplamı 1.0'a kadar olmalıdır. Bu ek kısıtlama, eğitimin daha hızlı yakınsamasını sağlar.
void Softmax(double &oSums[],double &_softOut[]) { // determine max output sum // does all output nodes at once so scale doesn't have to be re-computed each time int size=ArraySize(oSums); double max= oSums[0]; for(int i = 0; i<size;++i) if(oSums[i]>max) max=oSums[i]; // determine scaling factor -- sum of exp(each val - max) double scale=0.0; for(int i= 0; i<size;++i) scale+= MathExp(oSums[i]-max); ArrayResize(_softOut,size); for(int i=0; i<size;++i) _softOut[i]=MathExp(oSums[i]-max)/scale; }
4. DeepNeuralNetwork sınıfını kullanan demo Uzman Danışman
Uzman Danışmanı geliştirmeye başlamadan önce Derin Sinir Ağımıza girdi olarak hangi verileri sağlayacağımızı tanımlamamız gerekiyor. Sinir ağları, modelleri sınıflandırmada iyi olduğu için, Japon mumunun göreli değerlerini girdi olarak kullanacağız. Bu değerler, üst gölgenin, gövdenin ve alt gölgenin boyutu ve mumun yönü (boğa tipi veya ayı tipi) olacaktır. Girdi sayısı mutlaka az olmak zorunda değildir, ancak bizim durumumuzda test programı için yeterli olacaktır.
Demo Uzman Danışman:
4-4-5-3 sinir ağı yapısı, toplam (4 * 4) + 4 + (4 * 5) + 5 + (5 * 3) + 3 = 63 ağırlık ve bias değeri gerektirir.
#include <DeepNeuralNetwork.mqh> int numInput=4; int numHiddenA = 4; int numHiddenB = 5; int numOutput=3; DeepNeuralNetwork dnn(numInput,numHiddenA,numHiddenB,numOutput); //--- weight & bias values input double w0=1.0; input double w1=1.0; input double w2=1.0; input double w3=1.0; input double w4=1.0; input double w5=1.0; input double w6=1.0; input double w7=1.0; input double w8=1.0; input double w9=1.0; input double w10=1.0; input double w11=1.0; input double w12=1.0; input double w13=1.0; input double w14=1.0; input double w15=1.0; input double b0=1.0; input double b1=1.0; input double b2=1.0; input double b3=1.0; input double w40=1.0; input double w41=1.0; input double w42=1.0; input double w43=1.0; input double w44=1.0; input double w45=1.0; input double w46=1.0; input double w47=1.0; input double w48=1.0; input double w49=1.0; input double w50=1.0; input double w51=1.0; input double w52=1.0; input double w53=1.0; input double w54=1.0; input double w55=1.0; input double w56=1.0; input double w57=1.0; input double w58=1.0; input double w59=1.0; input double b4=1.0; input double b5=1.0; input double b6=1.0; input double b7=1.0; input double b8=1.0; input double w60=1.0; input double w61=1.0; input double w62=1.0; input double w63=1.0; input double w64=1.0; input double w65=1.0; input double w66=1.0; input double w67=1.0; input double w68=1.0; input double w69=1.0; input double w70=1.0; input double w71=1.0; input double w72=1.0; input double w73=1.0; input double w74=1.0; input double b9=1.0; input double b10=1.0; input double b11=1.0;
Sinir ağımızın girdileri için, mumun her bir parçasının mumun toplam büyüklüğünün yüzde kaçı olduğunu belirlemek adına aşağıdaki formülü kullanacağız.
//+------------------------------------------------------------------+ //|percentage of each part of the candle respecting total size | //+------------------------------------------------------------------+ int CandlePatterns(double high,double low,double open,double close,double uod,double &xInputs[]) { double p100=high-low;//Total candle size double highPer=0; double lowPer=0; double bodyPer=0; double trend=0; if(uod>0) { highPer=high-close; lowPer=open-low; bodyPer=close-open; trend=1; } else { highPer=high-open; lowPer=close-low; bodyPer=open-close; trend=0; } if(p100==0)return(-1); xInputs[0]=highPer/p100; xInputs[1]=lowPer/p100; xInputs[2]=bodyPer/p100; xInputs[3]=trend; return(1); }
Artık girdileri sinir ağımız üzerinden işleyebiliriz:
MqlRates rates[]; ArraySetAsSeries(rates,true); int copied=CopyRates(_Symbol,0,1,5,rates); //Compute the percent of the upper shadow, lower shadow and body in base of sum 100% int error=CandlePatterns(rates[0].high,rates[0].low,rates[0].open,rates[0].close,rates[0].close-rates[0].open,_xValues); if(error<0)return; dnn.SetWeights(weight); double yValues[]; dnn.ComputeOutputs(_xValues,yValues);
Ardından, sinir ağı, alınan verilere dayalı olarak ticaret fırsatını hesaplar. Softmax fonksiyonunun %100 toplama dayalı olarak 3 çıktı üreteceğini unutmayın. Değerler yValues dizisinde depolanır ve %60'tan büyük değerde işlem gerçekleştirilir.
//--- if the output value of the neuron is mare than 60% if(yValues[0]>0.6) { if(m_Position.Select(my_symbol))//check if there is an open position { if(m_Position.PositionType()==POSITION_TYPE_SELL) m_Trade.PositionClose(my_symbol);//Close the opposite position if exists if(m_Position.PositionType()==POSITION_TYPE_BUY) return; } m_Trade.Buy(lot_size,my_symbol);//open a Long position } //--- if the output value of the neuron is mare than 60% if(yValues[1]>0.6) { if(m_Position.Select(my_symbol))//check if there is an open position { if(m_Position.PositionType()==POSITION_TYPE_BUY) m_Trade.PositionClose(my_symbol);//Close the opposite position if exists if(m_Position.PositionType()==POSITION_TYPE_SELL) return; } m_Trade.Sell(lot_size,my_symbol);//open a Short position } if(yValues[2]>0.6) { m_Trade.PositionClose(my_symbol);//close any position }
5. Strateji optimizasyonunu kullanarak Derin Sinir Ağını eğitme
Fark etmişsinizdir ki, şu ana kadar yalnızca Derin Sinir Ağı ileri besleme mekanizması uyguladık, ancak herhangi bir eğitim gerçekleştirilmedi. Eğitim, strateji sınayıcıda gerçekleştirilir. Aşağıda size sinir ağını nasıl eğiteceğinizi göstereceğim. Lütfen çok sayıda girdi ve eğitim parametreleri aralığı nedeniyle eğitimin yalnızca MetaTrader 5'te gerçekleştirilebileceğini unutmayın. Gerekirse sonrasında, elde edilen optimizasyon değerleri MetaTrader 4'e kolayca kopyalanabilir.
Strateji Sınayıcı yapılandırması:
Eğitim için ağırlık ve bias aralığı 0,1, 0,01 veya 0,001'lik adımlarla -1 ila 1 arasında olabilir. Bu değerleri deneyebilir ve hangisinin en iyi sonucu verdiğini görebilirsiniz. Benim durumumda, aşağıdaki görüntüde görüldüğü gibi 0.001 adımını kullandım:
Son kapanan mumu kullandığım için "Sadece açılış fiyatları"nı seçtiğimi lütfen unutmayın, bu durum için her tiki kontrol etmeye gerek yok. H4 zaman diliminde optimizasyon gerçekleştirdim. Geçen yıl için geriye dönük test sırasında elde edilen sonuçlar:
Sonuç
Bu makalede sunulan kod ve açıklama, iki gizli katmana sahip sinir ağlarını anlamanız için size iyi bir temel oluşturabilir. Peki ya üç veya daha fazla gizli katmana sahip sinir ağları? Literatür araştırmasına dayalı olarak, neredeyse tüm pratik problemler için iki gizli katmanın yeterli olduğu konusunda bir fikir birliği vardır. Bu makale, Derin Sinir Ağlarını kullanarak fiyat tahmini için iyileştirilmiş modelleri geliştirmeye yönelik yaklaşımı açıklar. Bu, derin ağların ham verilerden soyut özellikleri öğrenme yeteneğine dayanır. Ön sonuçlar, derin ağımızın gelişmiş döviz piyasaları için temel modellerden önemli ölçüde daha yüksek tahmin doğruluğu sağladığını doğrulamaktadır.
MetaQuotes Ltd tarafından İngilizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/en/articles/5486
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz
Aşağıdaki işlevi 'void' yerine 'bool' döndürmek için güncelleyin ve verilen ağırlıkların çok az olduğunu göreceksiniz.
Ayrıca dosyanın üstündeki ağırlıkları da güncellemeniz gerektiğini unutmayın (sadece ağı başlattığınızda güncellemeniz yeterli değildir :P
Bu kodlanmış bir NN, daha sonra eğitemeyeceğiniz için nöron sayısını artırmanızı önermiyorum...
Paylaşmış olduğunuz kodlar için teşekkürler. Yeni bir kodlayıcı olarak, sormak istediğim bazı sorular var, umarım deneyimlerinizden yardım alabilirim. Şimdiden teşekkürler.
1. " DeepNeuralNetwork.mqh " dosyasını derlediğimde bunun neden olduğunu anlayamadım. Lütfen aşağıdaki ekran görüntüsüne bakın.
2. DemoEADNN.mq5'te bu iki dosya nerede? Aşağıdaki ekran görüntüsüne bakın.
Teşekkürler.
1. "DeepNeuralNetwork.mqh" dosyasını derlerken bazı hatalar alıyorum ve düzeltemedim.
2. DemoEADNN.mq5'te bu iki dosyayı bulamadı, aşağıdaki ekran görüntüsüne bakın.
Teşekkürler.
Çok faydalı bir makale
Çok Teşekkürler