Aracılığıyla paylaş


Yinelenen Ağlarla Uygulamalı Laboratuvarlar Language Understanding

Bu öğreticinin en son ana sürümü veya yakında yayınlanacak olan yaklaşan CNTK 1.7.1 sürümünü gerektirdiğini unutmayın.

Bu uygulamalı laboratuvarda, Air Travel Information Services (ATIS) yuva etiketleme ve amaç sınıflandırma görevleri için metni işlemek için yinelenen bir ağın nasıl uygulandığı gösterilmektedir. Düz ileriye doğru ekleme ve ardından yinelenen bir LSTM ile başlayacağız. Ardından bunu komşu sözcükleri içerecek şekilde genişletecek ve çift yönlü olarak çalıştıracağız. Son olarak, bu sistemi bir amaç sınıflandırıcısına dönüştüreceğiz.

Uygulayacağınız teknikler şunlardır:

  • formül yazmak yerine katman blokları oluşturarak model açıklaması
  • kendi katman bloğunuzu oluşturma
  • aynı ağdaki farklı sıra uzunluklarına sahip değişkenler
  • paralel eğitim

Derin öğrenmenin temellerini ve bu belirli kavramları bildiğinizi varsayıyoruz:

Önkoşullar

CNTK'yi zaten yüklediğinizi ve CNTK komutunu çalıştırabileceğinizi varsayıyoruz. Bu öğretici KDD 2016'da gerçekleştirildi ve yeni bir derleme gerektiriyor, kurulum yönergeleri için lütfen buraya bakın . Bu sayfadan ikili yükleme paketi indirme yönergelerini izleyebilirsiniz.

Ardından, lütfen bir ZIP arşivi indirin (yaklaşık 12 MB): Bu bağlantıya tıklayın ve ardından İndir düğmesine tıklayın. Arşiv, bu öğreticinin dosyalarını içerir. Lütfen arşive gidin ve çalışma dizininizi olarak SLUHandsOnayarlayın. Üzerinde çalışacağınız dosyalar şunlardır:

Son olarak, bunu CUDA uyumlu gpu özellikli bir makinede çalıştırmanızı kesinlikle öneririz. GPU'lar olmadan derin öğrenme eğlenceli değildir.

Görev ve Model Yapısı

Bu öğreticide yaklaşmak istediğimiz görev yuva etiketlemedir. ATIS corpus'unu kullanıyoruz. ATIS, Air Travel Information Services etki alanından insan bilgisayarı sorguları içerir ve görevimiz, belirli bir bilgi öğesine (yuva) ait olup olmadığını ve hangisinin sorgunun her sözcüğüne açıklama eklemek (etiketlemek) olacaktır.

Çalışma klasörünüzdeki veriler zaten "CNTK Metin Biçimi"ne dönüştürüldü. Şimdi test kümesi dosyasından atis.test.ctfbir örneğe bakalım:

19  |S0 178:1 |# BOS      |S1 14:1 |# flight  |S2 128:1 |# O
19  |S0 770:1 |# show                         |S2 128:1 |# O
19  |S0 429:1 |# flights                      |S2 128:1 |# O
19  |S0 444:1 |# from                         |S2 128:1 |# O
19  |S0 272:1 |# burbank                      |S2 48:1  |# B-fromloc.city_name
19  |S0 851:1 |# to                           |S2 128:1 |# O
19  |S0 789:1 |# st.                          |S2 78:1  |# B-toloc.city_name
19  |S0 564:1 |# louis                        |S2 125:1 |# I-toloc.city_name
19  |S0 654:1 |# on                           |S2 128:1 |# O
19  |S0 601:1 |# monday                       |S2 26:1  |# B-depart_date.day_name
19  |S0 179:1 |# EOS                          |S2 128:1 |# O

Bu dosyanın 7 sütunu vardır:

  • bir sıra kimliği (19). Bu sıra kimliğine sahip 11 giriş vardır. Bu, sıra 19'un 11 belirteçlerden oluştuğu anlamına gelir;
  • sütun S0, sayısal sözcük dizinleri içeren;
  • bir insan okuyucunun sayısal sözcük dizininin anlamını bilmesini sağlamak için tarafından #belirtilen bir yorum sütunu; Açıklama sütunları sistem tarafından yoksayılır. BOS ve EOS sırasıyla cümlenin başlangıcını ve sonunu belirtmek için özel sözcüklerdir;
  • sütunu S1 , yalnızca öğreticinin son bölümünde kullanacağımız bir amaç etiketidir;
  • sayısal amaç dizininin insan tarafından okunabilir etiketini gösteren başka bir açıklama sütunu;
  • sütunu S2 , sayısal dizin olarak temsil edilen yuva etiketidir; ve
  • sayısal etiket dizininin insan tarafından okunabilir etiketini gösteren başka bir açıklama sütunu.

Sinir ağının görevi, sorguya (sütun S0) bakmak ve yuva etiketini (sütun S2) tahmin etmektir. Gördüğünüz gibi, girişteki her sözcük boş bir etikete O veya ilk sözcük için ile B- başlayan bir yuva etiketine ve aynı yuvaya ait diğer ardışık sözcükler için ile I- atanır.

Kullanacağımız model, ekleme katmanı, yinelenen LSTM hücresi ve son olasılıkları hesaplamak için yoğun bir katmandan oluşan yinelenen bir modeldir:

slot label   "O"        "O"        "O"        "O"  "B-fromloc.city_name"
              ^          ^          ^          ^          ^
              |          |          |          |          |
          +-------+  +-------+  +-------+  +-------+  +-------+
          | Dense |  | Dense |  | Dense |  | Dense |  | Dense |  ...
          +-------+  +-------+  +-------+  +-------+  +-------+
              ^          ^          ^          ^          ^
              |          |          |          |          |
          +------+   +------+   +------+   +------+   +------+   
     0 -->| LSTM |-->| LSTM |-->| LSTM |-->| LSTM |-->| LSTM |-->...
          +------+   +------+   +------+   +------+   +------+   
              ^          ^          ^          ^          ^
              |          |          |          |          |
          +-------+  +-------+  +-------+  +-------+  +-------+
          | Embed |  | Embed |  | Embed |  | Embed |  | Embed |  ...
          +-------+  +-------+  +-------+  +-------+  +-------+
              ^          ^          ^          ^          ^
              |          |          |          |          |
w      ------>+--------->+--------->+--------->+--------->+------... 
             BOS      "show"    "flights"    "from"   "burbank"

Veya bir CNTK ağ açıklaması olarak. Lütfen hızlı bir bakış atın ve yukarıdaki açıklamayla eşleştirin:

    model = Sequential (
        EmbeddingLayer {150} :
        RecurrentLSTMLayer {300} :
        DenseLayer {labelDim}
    )

Bu işlevlerin açıklamaları şu konumda bulunabilir: Sequential(), EmbeddingLayer{}, RecurrentLSTMLayer{}ve DenseLayer{}

CNTK Yapılandırması

Yapılandırma Dosyası

CNTK'de bir modeli eğitmek ve test etmek için, CNTK'ye çalıştırmak istediğiniz işlemleri (command değişken) bildiren bir yapılandırma dosyası ve her komut için bir parametre bölümü sağlamamız gerekir.

Eğitim komutu için CNTK'ye şunun söylenmesi gerekir:

  • verileri okuma (reader bölüm)
  • model işlevi ve hesaplama grafında (BrainScriptNetworkBuilder bölüm) giriş ve çıkışları
  • öğrenci için hiper parametreler (SGD bölüm)

Değerlendirme komutu için CNTK'nin test verilerinin (reader bölümü) nasıl okunması gerektiğini bilmesi gerekir.

Aşağıda, başlayacağımız yapılandırma dosyası yer alır. Gördüğünüz gibi, CNTK yapılandırma dosyası, bir kayıt hiyerarşisinde düzenlenmiş parametre tanımlarından oluşan bir metin dosyasıdır. Söz dizimini kullanarak CNTK'nin temel parametre değiştirme işlemini nasıl desteklediğini $parameterName$ de görebilirsiniz. Gerçek dosya yukarıda belirtilenden yalnızca birkaç tane daha fazla parametre içeriyor, ancak lütfen dosyayı tarayın ve az önce bahsedilen yapılandırma öğelerini bulun:

# CNTK Configuration File for creating a slot tagger and an intent tagger.

command = TrainTagger:TestTagger

makeMode = false ; traceLevel = 0 ; deviceId = "auto"

rootDir = "." ; dataDir  = "$rootDir$" ; modelDir = "$rootDir$/Models"

modelPath = "$modelDir$/slu.cmf"

vocabSize = 943 ; numLabels = 129 ; numIntents = 26    # number of words in vocab, slot labels, and intent labels

# The command to train the LSTM model
TrainTagger = {
    action = "train"
    BrainScriptNetworkBuilder = {
        inputDim = $vocabSize$
        labelDim = $numLabels$
        embDim = 150
        hiddenDim = 300

        model = Sequential (
            EmbeddingLayer {embDim} :                            # embedding
            RecurrentLSTMLayer {hiddenDim, goBackwards=false} :  # LSTM
            DenseLayer {labelDim}                                # output layer
        )

        # features
        query      = Input {inputDim}
        slotLabels = Input {labelDim}

        # model application
        z = model (query)

        # loss and metric
        ce   = CrossEntropyWithSoftmax (slotLabels, z)
        errs = ClassificationError     (slotLabels, z)

        featureNodes    = (query)
        labelNodes      = (slotLabels)
        criterionNodes  = (ce)
        evaluationNodes = (errs)
        outputNodes     = (z)
    }

    SGD = {
        maxEpochs = 8 ; epochSize = 36000

        minibatchSize = 70

        learningRatesPerSample = 0.003*2:0.0015*12:0.0003
        gradUpdateType = "fsAdaGrad"
        gradientClippingWithTruncation = true ; clippingThresholdPerSample = 15.0

        firstMBsToShowResult = 10 ; numMBsToShowResult = 100
    }

    reader = {
        readerType = "CNTKTextFormatReader"
        file = "$DataDir$/atis.train.ctf"
        randomize = true
        input = {
            query        = { alias = "S0" ; dim = $vocabSize$ ;  format = "sparse" }
            intentLabels = { alias = "S1" ; dim = $numIntents$ ; format = "sparse" }
            slotLabels   = { alias = "S2" ; dim = $numLabels$ ;  format = "sparse" }
        }
    }
}

# Test the model's accuracy (as an error count)
TestTagger = {
    action = "eval"
    modelPath = $modelPath$
    reader = {
        readerType = "CNTKTextFormatReader"
        file = "$DataDir$/atis.test.ctf"
        randomize = false
        input = {
            query        = { alias = "S0" ; dim = $vocabSize$ ;  format = "sparse" }
            intentLabels = { alias = "S1" ; dim = $numIntents$ ; format = "sparse" }
            slotLabels   = { alias = "S2" ; dim = $numLabels$ ;  format = "sparse" }
        }
    }
}

Veri ve Veri Okuma'ya Kısa Bir Bakış

Verilere zaten baktık. Peki bu biçimi nasıl oluşturursunuz? Bu öğreticide metin okumak için kullanılır CNTKTextFormatReader. Giriş verilerinin burada açıklanan belirli bir biçimde olmasını bekler.

Bu öğretici için corpora'yı iki adımda oluşturduk:

  • ham verileri, boşlukla ayrılmış metnin SEKMEyle ayrılmış sütunlarını içeren bir düz metin dosyasına dönüştürün. Örneğin:

    BOS show flights from burbank to st. louis on monday EOS (TAB) flight (TAB) O O O O B-fromloc.city_name O B-toloc.city_name I-toloc.city_name O B-depart_date.day_name O
    

    Bunun komutun çıkışıyla paste uyumlu olması amaçlanır.

  • aşağıdaki komutla bunu CNTK Metin Biçimi'ne (CTF) dönüştürün:

    python Scripts/txt2ctf.py --map query.wl intent.wl slots.wl --annotated True --input atis.test.txt --output atis.test.ctf
    

    burada üç .wl dosya kelime dağarcığını her sözcük için bir satır olacak şekilde düz metin dosyaları olarak verir.

Bu CTFG dosyalarında sütunlarımız , S1ve S2etiketlenmiştirS0. Bunlar gerçek ağ girişlerine okuyucu tanımında karşılık gelen satırlar tarafından bağlanır:

input = {
    query        = { alias = "S0" ; dim = $vocabSize$ ;  format = "sparse" }
    intentLabels = { alias = "S1" ; dim = $numIntents$ ; format = "sparse" }
    slotLabels   = { alias = "S2" ; dim = $numLabels$ ;  format = "sparse" }
}

Çalıştırma

Yukarıdaki yapılandırma dosyasını çalışma klasöründeki adın SLUHandsOn.cntk altında bulabilirsiniz. Çalıştırmak için lütfen yukarıdaki yapılandırmayı şu komutla yürütür:

cntk  configFile=SLUHandsOn.cntk

Bu, adlı TrainTaggerbölümde tanımlandığı gibi model eğitimiyle başlayarak yapılandırmamızı yürütür. Biraz geveze bir ilk günlük çıkışının ardından, kısa bir süre sonra şunu göreceksiniz:

Training 721479 parameters in 6 parameter tensors.

ve ardından şuna benzer bir çıkışla devam edin:

Finished Epoch[ 1 of 8]: [Training] ce = 0.77274927 * 36007; errs = 15.344% * 36007
Finished Epoch[ 2 of 8]: [Training] ce = 0.27009664 * 36001; errs = 5.883% * 36001
Finished Epoch[ 3 of 8]: [Training] ce = 0.16390425 * 36005; errs = 3.688% * 36005
Finished Epoch[ 4 of 8]: [Training] ce = 0.13121604 * 35997; errs = 2.761% * 35997
Finished Epoch[ 5 of 8]: [Training] ce = 0.09308497 * 36000; errs = 2.028% * 36000
Finished Epoch[ 6 of 8]: [Training] ce = 0.08537533 * 35999; errs = 1.917% * 35999
Finished Epoch[ 7 of 8]: [Training] ce = 0.07477648 * 35997; errs = 1.686% * 35997
Finished Epoch[ 8 of 8]: [Training] ce = 0.06114417 * 36018; errs = 1.380% * 36018

Bu, öğrenmenin dönemler boyunca nasıl ilerleyişini gösterir (verilerden geçer). Örneğin, iki dönem sonra, yapılandırma dosyasında adlandırdığımız ce çapraz entropi ölçütü, bu çağın 36001 örneğinde ölçülen 0,27'ye ulaşmıştır ve aynı 36016 eğitim örneklerinde hata oranı %5,883'tür.

36001, yapılandırmamızın dönem boyutunu 36000 olarak tanımlamasından kaynaklanmaktadır. Dönem boyutu, model denetim noktaları arasında işlem yapmak için sözcük belirteci olarak sayılan örneklerin sayısıdır. Cümlelerin uzunluğu farklı olduğundan ve kesin olarak 36000 sözcüğün katlarına kadar toplam gerekmediğinden, bazı küçük çeşitlemeler göreceksiniz.

Eğitim tamamlandıktan sonra (titan-X veya Surface Book 2 dakikadan az), CNTK eylemle EvalTagger devam eder

Final Results: Minibatch[1-1]: errs = 2.922% * 10984; ce = 0.14306181 * 10984; perplexity = 1.15380111

Test kümemizde yuva etiketleri %2,9 hata oranıyla tahmin edilmiştir. Hiç fena değil, böyle basit bir sistem için!

Yalnızca CPU kullanan bir makinede 4 veya daha fazla kez daha yavaş olabilir. Sistemin ilerlediğinden erken emin olmak için izlemeyi etkinleştirerek kısmi sonuçları görebilirsiniz ve bu da makul ölçüde hızlı görünmelidir:

cntk  configFile=SLUHandsOn.cntk  traceLevel=1

Epoch[ 1 of 8]-Minibatch[   1-   1, 0.19%]: ce = 4.86535690 * 67; errs = 100.000% * 67 
Epoch[ 1 of 8]-Minibatch[   2-   2, 0.39%]: ce = 4.83886670 * 63; errs = 57.143% * 63
Epoch[ 1 of 8]-Minibatch[   3-   3, 0.58%]: ce = 4.78657442 * 68; errs = 36.765% * 68
...

Bu işlem bitene kadar beklemek istemiyorsanız, örneğin bir ara model çalıştırabilirsiniz.

cntk  configFile=SLUHandsOn.cntk  command=TestTagger  modelPath=Models/slu.cmf.4
Final Results: Minibatch[1-1]: errs = 3.851% * 10984; ce = 0.18932937 * 10984; perplexity = 1.20843890

veya çalışma klasöründe bulabileceğiniz önceden eğitilmiş modelimizi de test edin:

cntk  configFile=SLUHandsOn.cntk  command=TestTagger  modelPath=slu.forward.nobn.cmf
Final Results: Minibatch[1-1]: errs = 2.922% * 10984; ce = 0.14306181 * 10984; perplexity = 1.15380111

Modeli Değiştirme

Aşağıda, size CNTK yapılandırmalarını değiştirme alıştırması yapacak görevler verilecektir. Çözümler bu belgenin sonunda verilmiştir... ama lütfen olmadan deneyin!

Hakkında Bir Sözcük Sequential()

Görevlere atlamadan önce yeni çalıştırdığımız modele bir kez daha göz atalım. Model, işlev oluşturma stili olarak adlandırdığımız şekilde açıklanmıştır.

    model = Sequential (
        EmbeddingLayer {embDim} :                            # embedding
        RecurrentLSTMLayer {hiddenDim, goBackwards=false} :  # LSTM
        DenseLayer {labelDim, initValueScale=7}              # output layer
    )

burada iki nokta üst üste (:), BrainScript'in dizileri ifade etme söz dizimidir. Örneğin, (F:G:H) üç öğesi olan bir dizidir: F, Gve H.

Diğer sinir ağı araç setlerinden "sıralı" gösterimi hakkında bilgi sahibi olabilirsiniz. Aksi takdirde, Sequential() bir girişin katmanların ilerlemesi yoluyla yayılarak işlendiği sinir ağlarında çok yaygın bir durumu kısa bir şekilde ifade etmeye olanak tanıyan güçlü bir işlemdir. Sequential() bağımsız değişkeni olarak bir işlev dizisi alır ve bir işlevin çıkışını her seferinde bir sonrakine geçirerek bu işlevi sırayla çağıran yeni bir işlev döndürür. Örneğin,

FGH = Sequential (F:G:H)
y = FGH (x)

ile aynı anlama gelir

y = H(G(F(x))) 

Bu "işlev bileşimi" olarak bilinir ve genellikle bu forma sahip olan sinir ağlarını ifade etmek için özellikle kullanışlıdır:

     +-------+   +-------+   +-------+
x -->|   F   |-->|   G   |-->|   H   |--> y
     +-------+   +-------+   +-------+

Eldeki modelimize geri döndüğünüzde Sequential ifade yalnızca modelimizin şu forma sahip olduğunu söyler:

     +-----------+   +----------------+   +------------+
x -->| Embedding |-->| Recurrent LSTM |-->| DenseLayer |--> y
     +-----------+   +----------------+   +------------+

Görev 1: Batch Normalleştirmesi Ekleme

Şimdi modele, özellikle toplu normalleştirmeye yeni katmanlar eklemek istiyoruz.

Toplu normalleştirme, yakınsama hızını artırmaya yönelik popüler bir tekniktir. Genellikle görüntü işleme kurulumları için kullanılır, örneğin görüntü tanıma ile ilgili diğer uygulamalı laboratuvarımız. Yinelenen modellerde de kullanılabilir mi?

Bu nedenle göreviniz, yinelenen LSTM katmanından önce ve sonra toplu normalleştirme katmanları eklemek olacaktır. Görüntü işlemeyle ilgili uygulamalı laboratuvarları tamamladıysanız, toplu normalleştirme katmanının şu forma sahip olduğunu anımsayabilirsiniz:

BatchNormalizationLayer{}

Bu nedenle lütfen yapılandırmayı değiştirin ve neler olduğunu görün.

Her şey yolunda giderse, önceki yapılandırmayla karşılaştırıldığında yalnızca yakınsama hızının (ce ve errs) değil, aynı zamanda %2,0'lık daha iyi bir hata oranı (%2,9 ile karşılaştırıldığında) fark edeceksiniz:

Training 722379 parameters in 10 parameter tensors.

Finished Epoch[ 1 of 8]: [Training] ce = 0.29396894 * 36007; errs = 5.621% * 36007 
Finished Epoch[ 2 of 8]: [Training] ce = 0.10104186 * 36001; errs = 2.280% * 36001
Finished Epoch[ 3 of 8]: [Training] ce = 0.05012737 * 36005; errs = 1.258% * 36005
Finished Epoch[ 4 of 8]: [Training] ce = 0.04116407 * 35997; errs = 1.108% * 35997
Finished Epoch[ 5 of 8]: [Training] ce = 0.02602344 * 36000; errs = 0.756% * 36000
Finished Epoch[ 6 of 8]: [Training] ce = 0.02234042 * 35999; errs = 0.622% * 35999
Finished Epoch[ 7 of 8]: [Training] ce = 0.01931362 * 35997; errs = 0.667% * 35997
Finished Epoch[ 8 of 8]: [Training] ce = 0.01714253 * 36018; errs = 0.522% * 36018

Final Results: Minibatch[1-1]: errs = 2.039% * 10984; ce = 0.12888706 * 10984; perplexity = 1.13756164

(Eğitimin tamamlanmasını beklemek istemiyorsanız, sonuçta elde edilen modeli adının slu.forward.cmfaltında bulabilirsiniz.)

Lütfen çözüme buradan bakın.

Görev 2: Lookahead ekleme

Yinelenen modelimiz yapısal bir eksiklik yaşıyor: Yinelenme soldan sağa doğru çalıştığından, yuva etiketine ilişkin kararda yaklaşan sözcükler hakkında hiçbir bilgi yoktur. Model biraz eğimli. Göreviniz modeli, yinelenme girişi yalnızca geçerli sözcüğü değil, aynı zamanda bir sonraki sözcüğü de (lookahead) oluşacak şekilde değiştirmek olacaktır.

Çözümünüz işlev kompozisyonu stilinde olmalıdır. Bu nedenle, aşağıdakileri yapacak bir BrainScript işlevi yazmanız gerekir:

  • bir giriş bağımsız değişkeni kabul etme;
  • işlevini kullanarak FutureValue() bu girişin hemen "gelecekteki değerini" hesaplayın (şu özel formu kullanın: FutureValue (0, input, defaultHiddenActivation=0)); ve
  • kullanarak Splice() ikisini katıştırma boyutunun iki katı bir vektörde birleştirin (şu formu kullanın: Splice (x:y))

ve ardından bu işlevi Sequence() ekleme ve yinelenen katman arasına ekleyin. Her şey yolunda giderse aşağıdaki çıkışı görürsünüz:

Training 902679 parameters in 10 parameter tensors.

Finished Epoch[ 1 of 8]: [Training] ce = 0.30500536 * 36007; errs = 5.904% * 36007
Finished Epoch[ 2 of 8]: [Training] ce = 0.09723847 * 36001; errs = 2.167% * 36001
Finished Epoch[ 3 of 8]: [Training] ce = 0.04082365 * 36005; errs = 1.047% * 36005
Finished Epoch[ 4 of 8]: [Training] ce = 0.03219930 * 35997; errs = 0.867% * 35997
Finished Epoch[ 5 of 8]: [Training] ce = 0.01524993 * 36000; errs = 0.414% * 36000
Finished Epoch[ 6 of 8]: [Training] ce = 0.01367533 * 35999; errs = 0.383% * 35999
Finished Epoch[ 7 of 8]: [Training] ce = 0.00937027 * 35997; errs = 0.278% * 35997
Finished Epoch[ 8 of 8]: [Training] ce = 0.00584430 * 36018; errs = 0.147% * 36018

Final Results: Minibatch[1-1]: errs = 1.839% * 10984; ce = 0.12023170 * 10984; perplexity = 1.12775812

Bu işe yaradı! Bir sonraki sözcüğün ne olduğunu bilmek yuvalı etiketlemenin hata oranını %2,0'dan %1,84'e düşürmesine olanak tanır.

(Eğitimin tamamlanmasını beklemek istemiyorsanız, sonuçta elde edilen modeli adının slu.forward.lookahead.cmfaltında bulabilirsiniz.)

Lütfen çözüme buradan bakın.

Görev 3: Çift Yönlü Yinelenen Model

Aha, gelecekteki kelimelerin bilgisi yardımcı olur. Yani tek kelimelik bir kafa yerine neden cümlenin sonuna kadar ileriye bakmıyoruz? Çift yönlü bir model oluşturalım!

Göreviniz, veriler üzerinde hem ileri hem de geri özyineleme gerçekleştiren ve çıkış vektörlerini bir araya getiren yeni bir katman uygulamaktır.

Ancak, bunun çift yönlü katmanın öğrenilebilir model parametreleri içerdiğinden önceki görevden farklı olduğunu unutmayın. İşlev oluşturma stilinde, model parametreleriyle bir katman uygulamaya yönelik desen, işlev nesnesi oluşturan bir fabrika işlevi yazmaktır.

Functor olarak da bilinen işlev nesnesi, hem işlev hem de nesne olan bir nesnedir. Bu da veri içeren başka hiçbir şeyin işlevmiş gibi çağrılabileceği anlamına gelir.

Örneğin, LinearLayer{outDim} bir ağırlık matrisi W, bir sapma bve hesaplamak W * input + biçin başka bir işlev içeren bir işlev nesnesi döndüren bir fabrika işlevidir. Örneğin, deyiş LinearLayer{1024} bu işlev nesnesini oluşturur ve bu nesne de diğer işlevler gibi hemen kullanılabilir: LinearLayer{1024}(x).

Karıştı? Bir örnek alalım: Doğrusal katmanı sonraki toplu normalleştirmeyle birleştiren yeni bir katman uygulayalım. İşlev bileşimine izin vermek için katmanın aşağıdaki gibi görünebilecek bir fabrika işlevi olarak gerçekleştirilmesi gerekir:

LinearLayerWithBN {outDim} = {
    F = LinearLayer {outDim}
    G = BatchNormalization {normalizationTimeConstant=2048}
    apply (x) = G(F(x))
}.apply

Bu fabrika işlevini çağırmak önce üç üyeyle bir kayıt (ile {...}gösterilir) oluşturur: F, Gve apply. Bu örnekte F ve G işlevlerin kendileridir ve apply verilere uygulanacak işlevdir. .apply Bu ifadeye eklemek, bir kayıt üyesine erişmek için BrainScript'te her zaman ne anlama gelir.x? Bu nedenle, örneğin çağrısı, adlı Fbir doğrusal katman işlev nesnesi , batch-normalleştirme işlev nesnesi ve kullanarak GF bu katmanın gerçek işlemini uygulayan işlev olan bir nesne applyGoluşturur.LinearLayerWithBN{1024} Ardından döndürür apply. Dışarıdan bakıldığında, apply() bir işlev gibi görünür ve davranır. Bununla birlikte, arka planda ait apply() olduğu kaydı tutar ve böylece ve Görneklerine F erişimi korur.

Şimdi işimize geri dönelim. Şimdi yukarıdaki örnekte olduğu gibi bir fabrika işlevi oluşturmanız gerekir. İki yinelenen katman örneği (bir ileri, bir geri) oluşturan ve ardından her iki katman örneğini de aynıya x uygulayan ve iki sonucu birleştiren bir işlev tanımlayan bir apply (x) fabrika işlevi oluşturacaksınız.

Tamam, bir deneyin! CNTK'de geriye dönük özyineleme gerçekleştirmeyi öğrenmek için lütfen ileriye dönük özyinelemenin nasıl yapıldığına ilişkin bir ipucu alın. Lütfen aşağıdakileri de yapın:

  • yerine getirmeyi hedeflediğimiz, önceki göreve eklediğiniz tek sözcüklü lookahead'i kaldırın; Ve
  • hiddenDim toplam model parametresi sayısını sınırlı tutmak için parametresini 300'den 150'ye değiştirin.

Bu modelin başarıyla çalıştırılması aşağıdaki çıkışı oluşturur:

Training 542379 parameters in 13 parameter tensors.

Finished Epoch[ 1 of 8]: [Training] ce = 0.27651655 * 36007; errs = 5.288% * 36007
Finished Epoch[ 2 of 8]: [Training] ce = 0.08179804 * 36001; errs = 1.869% * 36001
Finished Epoch[ 3 of 8]: [Training] ce = 0.03528780 * 36005; errs = 0.828% * 36005
Finished Epoch[ 4 of 8]: [Training] ce = 0.02602517 * 35997; errs = 0.675% * 35997
Finished Epoch[ 5 of 8]: [Training] ce = 0.01310307 * 36000; errs = 0.386% * 36000
Finished Epoch[ 6 of 8]: [Training] ce = 0.01310714 * 35999; errs = 0.358% * 35999
Finished Epoch[ 7 of 8]: [Training] ce = 0.00900459 * 35997; errs = 0.300% * 35997
Finished Epoch[ 8 of 8]: [Training] ce = 0.00589050 * 36018; errs = 0.161% * 36018

Final Results: Minibatch[1-1]: errs = 1.830% * 10984; ce = 0.11924878 * 10984; perplexity = 1.12665017

Bir çekicilik gibi çalışıyor! Bu model %1,83'e ulaşıyor ve yukarıdaki lookahead modelinden biraz daha iyi. Çift yönlü model, lookahead modelinden %40 daha az parametreye sahiptir. Ancak, geri dönüp günlük çıkışının tamamına yakından bakarsanız (bu web sayfasında gösterilmez), arka plandakinin yaklaşık %30 daha hızlı eğitildiğini görebilirsiniz. Bunun nedeni, lookahead modelinin hem daha az yatay bağımlılıklara (iki yineleme yerine bir tane) hem de daha büyük matris ürünlerine sahip olması ve dolayısıyla daha yüksek paralellik elde edilebiliyor olmasıdır.

Lütfen çözüme buradan bakın.

Görev 4: Amaç Sınıflandırması

Şimdiye kadar oluşturduğunuz modelin bir amaç sınıflandırıcısına kolayca dönüştürülebileceği ortaya çıktı. Veri dosyamızda adlı S1ek sütunun bulunduğunu unutmayın. Bu sütun, sorgunun veya airfaregibi airport konularla ilgili bilgileri bulma amacını gösteren tümce başına tek bir etiket içerir.

Bir dizinin tamamını tek bir etikette sınıflandırma görevine sıralı sınıflandırma adı verilir. Sıralı sınıflandırıcımız, son adımının gizli durumunu aldığımız yinelenen bir LSTM (zaten buna sahibiz) olarak uygulanacaktır. Bu bize her dizi için tek bir vektör verir. Bu vektör daha sonra softmax sınıflandırması için yoğun bir katmana beslenir.

CNTK'nin adlı BS.Sequences.Last()bir diziden son durumu ayıklama işlemi vardır. Bu işlem, aynı minibatch'in çok farklı uzunluklarda diziler içerebileceği ve bellekte paketlenmiş bir biçimde düzenlendikleri gerçeğini kabul eder. Benzer şekilde, geriye dönük özyineleme için kullanabiliriz BS.Sequences.First().

Göreviniz, Görev 3'ten çift yönlü ağı, son kare ileriye doğru özyinelemeden ayıklanıp ilk kare geriye dönük özyinelemeden ayıklanıp iki vektör birleştirilir. Birleştirilmiş vektör (bazen düşünce vektör olarak da adlandırılır) daha sonra yoğun katmanın girişi olacaktır.

Ayrıca, etiketi yuvadan amaç etiketine değiştirmeniz gerekir: Bunun yerine giriş değişkenini (slotLabels) amaç etiketleri için okuyucu bölümünde kullanılan adla eşleşecek şekilde ve ayrıca boyutla eşleşecek şekilde yeniden adlandırın.

Lütfen değişikliği deneyin. Bunu doğru yaparsanız, bununla birlikte, bir vexing hata iletisiyle ve bu işlemde uzun bir hata iletisiyle karşılaşırsınız:

EXCEPTION occurred: Dynamic axis layout '*' is shared between inputs 'intentLabels'
and 'query', but layouts generated from the input data are incompatible on this axis.
Are you using different sequence lengths? Did you consider adding a DynamicAxis()
to the Input nodes?

"Farklı sıra uzunlukları mı kullanıyorsunuz?" Ah evet! Sorgu ve amaç etiketi bunu yapar; amaç etiketi sorgu başına tek bir belirteçtir. 1 öğeden oluşan bir dizidir! Peki bu nasıl düzeltilir?

CNTK, ağdaki farklı değişkenlerin farklı sıra uzunluklarına sahip olmasını sağlar. Sıra uzunluğunu ek, sembolik bir tensör boyutu olarak düşünebilirsiniz. Aynı uzunluktaki değişkenler aynı sembolik uzunluk boyutunu paylaşır. İki değişken farklı uzunluklara sahipse, bu açıkça bildirilmelidir, aksi takdirde CNTK tüm değişkenlerin aynı sembolik uzunluğu paylaştığını varsayar.

Bu, yeni bir dinamik eksen nesnesi oluşturup bunu girişlerden biriyle ilişkilendirerek yapılır:

    n = DynamicAxis()
    query = Input {inputDim, dynamicAxis=n}

CNTK'nin varsayılan ekseni vardır. Yukarıdaki özel durumdan tahmin edebileceğiniz gibi adı '*'.
Bu nedenle, yalnızca bir yeni eksen bildirmeniz gerekir; diğer Giriş (intentLabels) varsayılan ekseni kullanmaya devam eder.

Şimdi çalıştırmak ve aşağıdaki çıkışı görmek iyi olmalıdır:

Training 511376 parameters in 13 parameter tensors.

Finished Epoch[ 1 of 8]: [Training] ce = 1.17365003 * 2702; errs = 21.318% * 2702
Finished Epoch[ 2 of 8]: [Training] ce = 0.40112341 * 2677; errs = 9.189% * 2677
Finished Epoch[ 3 of 8]: [Training] ce = 0.17041608 * 2688; errs = 4.167% * 2688
Finished Epoch[ 4 of 8]: [Training] ce = 0.09521124 * 2702; errs = 2.739% * 2702
Finished Epoch[ 5 of 8]: [Training] ce = 0.08287138 * 2697; errs = 2.262% * 2697
Finished Epoch[ 6 of 8]: [Training] ce = 0.07138554 * 2707; errs = 2.032% * 2707
Finished Epoch[ 7 of 8]: [Training] ce = 0.06220047 * 2677; errs = 1.419% * 2677
Finished Epoch[ 8 of 8]: [Training] ce = 0.05072431 * 2686; errs = 1.340% * 2686

Final Results: Minibatch[1-1]: errs = 4.143% * 893; ce = 0.27832144 * 893; perplexity = 1.32091072

Fazla çaba harcamadan %4,1 hata oranına ulaştık. İlk atış için çok güzel (bu görevdeki sanatın %3'te olan durumu tam olmasa da).

Yine de bir şey fark edebilirsiniz: Dönem başına örnek sayısı şu anda 2700 civarındadır. Bunun nedeni, artık tümce başına yalnızca bir tane bulunan etiket örneklerinin sayısıdır. Bu görevde denetim sinyali sayısı büyük ölçüde azaldı. Bu, minibatch boyutunu artırmaya çalışmamızı teşvik etmelidir. Şimdi 70 yerine 256'sını deneyelim:

Finished Epoch[ 1 of 8]: [Training] ce = 1.11500325 * 2702; errs = 19.282% * 2702
Finished Epoch[ 2 of 8]: [Training] ce = 0.29961089 * 2677; errs = 6.052% * 2677
Finished Epoch[ 3 of 8]: [Training] ce = 0.09018802 * 2688; errs = 2.418% * 2688
Finished Epoch[ 4 of 8]: [Training] ce = 0.04838102 * 2702; errs = 1.258% * 2702
Finished Epoch[ 5 of 8]: [Training] ce = 0.02996789 * 2697; errs = 0.704% * 2697
Finished Epoch[ 6 of 8]: [Training] ce = 0.02142932 * 2707; errs = 0.517% * 2707
Finished Epoch[ 7 of 8]: [Training] ce = 0.01220149 * 2677; errs = 0.299% * 2677
Finished Epoch[ 8 of 8]: [Training] ce = 0.01312233 * 2686; errs = 0.186% * 2686

Bu sistem çok daha iyi öğrenir! (Bununla birlikte, bu farkın büyük olasılıkla gradyan normalleştirme şemamızdan fsAdagrad kaynaklanan bir yapıt olduğunu ve daha büyük veri kümeleri kullanılırken genellikle yakında kaybolacağını unutmayın.)

Ancak sonuçta elde edilen hata oranı daha yüksektir:

Final Results: Minibatch[1-1]: errs = 4.479% * 893; ce = 0.31638223 * 893; perplexity = 1.37215463

ancak bu fark aslında önemli olmayan 3 hataya karşılık gelir.

Lütfen çözüme buradan bakın.

Görev 5: Paralel Eğitim

Son olarak, birden çok GPU'nuz varsa, CNTK MPI (İleti Geçirme Arabirimi) kullanarak eğitimi paralelleştirmenize olanak tanır. Bu model herhangi bir hızlanma bekleyemeyecek kadar küçük; bu kadar küçük bir modeli paralel hale getirme, kullanılabilir GPU'ları ciddi şekilde az kullanır. Bununla birlikte, gerçek dünya iş yüklerine geçtikten sonra bunu nasıl yapacağınızı bilmeniz için hareketleri gözden geçirelim.

Lütfen bloğuna aşağıdaki satırları SGD ekleyin:

SGD = {
    ...
    parallelTrain = {
        parallelizationMethod = "DataParallelSGD"
        parallelizationStartEpoch = 1
        distributedMBReading = true
        dataParallelSGD = { gradientBits = 2 }
    }
}

ve ardından şu komutu yürütür:

mpiexec -np 4 cntk  configFile=SLUHandsOn_Solution4.cntk  stderr=Models/log  parallelTrain=true  command=TrainTagger

Bu, 1 bit SGD algoritmasını (aslında 2 bit SGD) kullanarak eğitimi 4 GPU üzerinde çalıştırır. Yaklaşık değeri doğruluğu incitmedi: Hata oranı %4,367, iki hata daha (lütfen eylemi tek bir GPU üzerinde ayrı olarak çalıştırın TestTagger ).

Sonuç

Bu öğreticide, ağları temsil eden kompakt bir araç olarak işlev oluşturma stili tanıtılmıştır. Birçok sinir ağı türü, bir grafiğin ağ açıklamasına daha doğrudan ve daha az hataya açık bir çevirisi olan bu şekilde temsil etme açısından uygundur.

Bu öğreticide, işlev oluşturma stilinde var olan bir yapılandırmayı almak ve bunu belirli şekillerde değiştirmek için alıştırma yapılır:

  • katman ekleme (önceden tanımlanmış katmanlar galerimizden)
  • işlev tanımlama ve kullanma
  • katman fabrikası işlevini tanımlama ve kullanma

Öğreticide birden çok zaman boyutunun işlenmesi de ele alınmıştı ve eğitimi nasıl paralelleştirebileceğimizi gördük.

Çözümler

Yukarıdaki görevlerin çözümleri aşağıdadır. Hile yok!

Çözüm 1: Batch Normalleştirmesi Ekleme

Değiştirilen model işlevi şu forma sahiptir:

    model = Sequential (
        EmbeddingLayer {embDim} :                            # embedding
        BatchNormalizationLayer {} :           ##### added
        RecurrentLSTMLayer {hiddenDim, goBackwards=false} :  # LSTM
        BatchNormalizationLayer {} :           ##### added
        DenseLayer {labelDim}                                # output layer
    )

Çözüm 2: Lookahead ekleme

Lookahead-işleviniz şu şekilde tanımlanabilir:

    OneWordLookahead (x) = Splice (x : DelayLayer {T=-1} (x))

ve modele şu şekilde eklenir:

    model = Sequential (
        EmbeddingLayer {embDim} :
        OneWordLookahead :                   ##### added
        BatchNormalizationLayer {} :
        RecurrentLSTMLayer {hiddenDim, goBackwards=false} :
        BatchNormalizationLayer {} :
        DenseLayer {labelDim}
    )

Çözüm 3: Çift Yönlü Yinelenen Model

Çift yönlü yinelenen katman şöyle yazılabilir:

    BiRecurrentLSTMLayer {outDim} = {
        F = RecurrentLSTMLayer {outDim, goBackwards=false}
        G = RecurrentLSTMLayer {outDim, goBackwards=true}
        apply (x) = Splice (F(x):G(x))
    }.apply

ve ardından şu şekilde kullanılır:

    hiddenDim = 150      ##### changed from 300 to 150

    model = Sequential (
        EmbeddingLayer {embDim} :
        ###OneWordLookahead :                   ##### removed
        BatchNormalizationLayer {} :
        BiRecurrentLSTMLayer {hiddenDim} :
        BatchNormalizationLayer {} :
        DenseLayer {labelDim}
    )

Çözüm 4: Amaç Sınıflandırması

Dizileri yinelenen katmanın son/ilk gizli dizisine küçültün:

        apply (x) = Splice (BS.Sequences.Last(F(x)):BS.Sequences.First(G(x)))
        ##### added Last() and First() calls ^^^

Yuvadaki etiket girişini amada değiştirin:

    intentDim = $numIntents$    ###### name change
    ...
        DenseLayer {intentDim}                      ##### different dimension
    ...
    intentLabels = Input {intentDim}
    ...
    ce   = CrossEntropyWithSoftmax (intentLabels, z)
    errs = ErrorPrediction         (intentLabels, z)
    ...
    labelNodes      = (intentLabels)

Yeni bir dinamik eksen kullanın:

    n = DynamicAxis()                               ##### added
    query        = Input {inputDim, dynamicAxis=n}  ##### use dynamic axis

Bildirim

Derek Liu'ya bu öğreticinin temelini hazırladığı için teşekkür ederiz.