되풀이 네트워크를 사용하는 실습 랩 Language Understanding

이 자습서에는 최신 마스터 버전 또는 곧 출시될 CNTK 1.7.1이 필요합니다.

이 실습 랩에서는 슬롯 태그 지정 및 의도 분류의 ATIS(항공 여행 정보 서비스) 작업에 대해 텍스트를 처리하는 되풀이 네트워크를 구현하는 방법을 보여 줍니다. 먼저 직선 포함 및 되풀이 LSTM으로 시작합니다. 그런 다음, 인접 단어를 포함하고 양방향으로 실행하도록 확장합니다. 마지막으로 이 시스템을 의도 분류자로 전환합니다.

연습할 기술은 다음과 같습니다.

  • 수식을 작성하는 대신 레이어 블록을 작성하여 모델 설명
  • 사용자 고유의 레이어 블록 만들기
  • 동일한 네트워크에서 시퀀스 길이가 다른 변수
  • 병렬 학습

딥 러닝의 기본 사항과 다음과 같은 특정 개념을 잘 알고 있다고 가정합니다.

필수 구성 요소

CNTK를 이미 설치했으며 CNTK 명령을 실행할 수 있다고 가정합니다. 이 자습서는 KDD 2016에서 개최되었으며 최신 빌드가 필요합니다. 설치 지침은 여기를 참조 하세요. 해당 페이지에서 이진 설치 패키지를 다운로드하기 위한 지침을 따를 수 있습니다.

다음으로 ZIP 보관 파일(약 12MB)을 다운로드하세요. 이 링크를 클릭한 다음 다운로드 단추를 클릭합니다. 보관 파일에는 이 자습서의 파일이 포함되어 있습니다. 보관하고 작업 디렉터리를 SLUHandsOn.로 설정하세요. 작업할 파일은 다음과 같습니다.

마지막으로, CUDA 호환 GPU가 지원되는 컴퓨터에서 이 기능을 실행하는 것이 좋습니다. GPU가 없는 딥 러닝은 재미있지 않습니다.

작업 및 모델 구조

이 자습서에서 접근하려는 작업은 슬롯 태그 지정입니다. ATIS 모음을 사용합니다. ATIS에는 항공 여행 정보 서비스 도메인의 인간-컴퓨터 쿼리가 포함되며, 특정 정보 항목(슬롯)에 속하는지 여부와 쿼리의 각 단어에 주석을 달고 태그를 지정하는 작업이 수행됩니다.

작업 폴더의 데이터가 이미 "CNTK 텍스트 형식"으로 변환되었습니다. 테스트 집합 파일 atis.test.ctf의 예제를 살펴보겠습니다.

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

이 파일에는 7개의 열이 있습니다.

  • 시퀀스 ID(19)입니다. 이 시퀀스 ID를 가진 11개의 항목이 있습니다. 즉, 시퀀스 19는 11개의 토큰으로 구성됩니다.
  • 숫자 단어 인덱스를 포함하는 열 S0
  • 사람이 숫자 단어 인덱스가 무엇을 나타내는지 알 수 있도록 나타내는 주석 열 #입니다. 주석 열은 시스템에서 무시됩니다. BOS 각각 EOS 문장의 시작과 끝을 나타내는 특수 단어입니다.
  • S1 은 자습서의 마지막 부분에서만 사용할 의도 레이블입니다.
  • 숫자 의도 인덱스의 사람이 읽을 수 있는 레이블을 보여 주는 다른 주석 열입니다.
  • S2 은 숫자 인덱스로 표시되는 슬롯 레이블이며,
  • 숫자 레이블 인덱스의 사람이 읽을 수 있는 레이블을 보여 주는 또 다른 주석 열입니다.

신경망의 작업은 쿼리(열)를 보고 슬롯 레이블(열 S0)을 예측하는 것입니다 S2. 보듯이 입력의 각 단어에는 첫 번째 단어로 B- 시작하는 빈 레이블 O 또는 슬롯 레이블이 할당되고 I- 동일한 슬롯에 속하는 추가 연속 단어에 대해 할당됩니다.

사용할 모델은 포함 계층, 되풀이 LSTM 셀 및 후방 확률을 계산하는 조밀한 계층으로 구성된 되풀이 모델입니다.

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"

또는 CNTK 네트워크 설명으로 사용합니다. 간단히 살펴보고 위의 설명과 일치하세요.

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

이러한 함수에 대한 설명은 다음EmbeddingLayer{}RecurrentLSTMLayer{}에서 Sequential()찾을 수 있습니다.DenseLayer{}

CNTK 구성

구성 파일

CNTK에서 모델을 학습하고 테스트하려면 실행하려는 작업(변수)command 을 CNTK에 알려주는 구성 파일과 모든 명령에 대한 매개 변수 섹션을 제공해야 합니다.

학습 명령의 경우 CNTK에 다음을 입력해야 합니다.

  • 데이터를 읽는 방법(reader 섹션)
  • 계산 그래프의 모델 함수 및 해당 입력 및 출력(BrainScriptNetworkBuilder 섹션)
  • 학습자에 대한 하이퍼 매개 변수(SGD 섹션)

평가 명령의 경우 CNTK는 테스트 데이터(reader 섹션)를 읽는 방법을 알고 있어야 합니다.

다음은 시작할 구성 파일입니다. 보듯이 CNTK 구성 파일은 레코드 계층 구조로 구성된 매개 변수 정의로 구성된 텍스트 파일입니다. CNTK가 구문을 사용하여 기본 매개 변수 대체를 $parameterName$ 지원하는 방법도 확인할 수 있습니다. 실제 파일에는 위에서 언급한 것보다 몇 가지 매개 변수만 더 포함되어 있지만, 검색하여 방금 언급한 구성 항목을 찾습니다.

# 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" }
        }
    }
}

데이터 및 데이터 읽기에 대한 간략한 정보

데이터를 이미 살펴보았습니다. 그러나 이 형식을 생성하려면 어떻게 해야 할까요? 텍스트를 읽기 위해 이 자습서에서는 .를 CNTKTextFormatReader사용합니다. 여기에 설명된 특정 형식의 입력 데이터가 있어야 합니다.

이 자습서에서는 다음 두 단계로 corpora를 만들었습니다.

  • 원시 데이터를 공백으로 구분된 텍스트의 TAB로 구분된 열이 포함된 일반 텍스트 파일로 변환합니다. 다음은 그 예입니다.

    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
    

    이는 명령의 출력과 호환되도록 하기 위한 것입니다 paste .

  • 다음 명령을 사용하여 CNTK CTF(텍스트 형식)로 변환합니다.

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

    여기서 세 .wl 파일은 단어당 한 줄씩 일반 텍스트 파일로 어휘를 제공합니다.

이러한 CTFG 파일에서 열에는 레이블이 지정됩니다S0S1S2. 판독기 정의의 해당 줄에 의해 실제 네트워크 입력에 연결됩니다.

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

실행 중

위의 구성 파일은 작업 폴더의 이름 SLUHandsOn.cntk 아래에서 찾을 수 있습니다. 실행하려면 다음 명령을 사용하여 위의 구성을 실행하세요.

cntk  configFile=SLUHandsOn.cntk

이렇게 하면 명명 TrainTagger된 섹션에 정의된 대로 모델 학습부터 구성이 실행됩니다. 다소 수다스러운 초기 로그 출력 후에 곧 다음이 표시됩니다.

Training 721479 parameters in 6 parameter tensors.

다음과 같은 출력이 뒤따릅니다.

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

이는 Epoch를 통해 학습이 진행되는 방식을 보여 줍니다(데이터를 통해 전달). 예를 들어 두 epoch 이후 구성 파일에서 명명 ce 한 크로스 엔트로피 기준은 이 epoch의 36001 샘플에서 측정한 대로 0.27에 도달했으며 동일한 36016 학습 샘플에서 오류율이 5.883%입니다.

36001은 구성에서 epoch 크기를 36000으로 정의했다는 사실에서 비롯됩니다. Epoch 크기는 모델 검사점 간에 처리하기 위해 문장이 아닌 단어 토큰으로 계산되는 샘플의 수입니다. 문장의 길이가 다르며 정확히 36000 단어의 배수로 요약할 필요는 없으므로 약간의 변형이 표시됩니다.

훈련이 완료되면(Titan-X 또는 Surface Book 2분 미만), CNTK는 작업을 진행 EvalTagger 합니다.

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

즉, 테스트 세트에서 슬롯 레이블은 2.9%의 오류율로 예측되었습니다. 전혀 나쁘지 않다, 이러한 간단한 시스템에 대한!

CPU 전용 컴퓨터에서는 4배 이상 느려질 수 있습니다. 시스템이 일찍 진행 중인지 확인하려면 추적을 사용하도록 설정하여 부분 결과를 확인할 수 있습니다. 이 결과는 합리적으로 빠르게 표시됩니다.

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
...

이 완료될 때까지 기다리지 않으려면 중간 모델(예:

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

또는 작업 폴더에서 찾을 수 있는 미리 학습된 모델을 테스트합니다.

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

모델 수정

다음에서는 CNTK 구성 수정을 연습하는 태스크가 제공됩니다. 해결 방법은 이 문서의 끝에 제공됩니다... 하지만 시도하지 마십시오!

단어 정보 Sequential()

작업으로 이동하기 전에 방금 실행한 모델을 다시 살펴보겠습니다. 이 모델은 함수-컴퍼지션 스타일에 설명되어 있습니다.

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

여기서 콜론(:)은 배열을 표현하는 BrainScript의 구문입니다. 예를 들어, (F:G:H) 세 개의 요소가 FG있는 배열, 및 H.

다른 신경망 도구 키트의 "순차적" 표기법을 잘 알고 있을 수 있습니다. 그렇지 않은 Sequential() 경우 한마디로 입력이 계층의 진행을 통해 전파하여 처리되는 신경망에서 매우 일반적인 상황을 압축적으로 표현할 수 있는 강력한 작업입니다. Sequential() 는 함수 배열을 인수로 사용하고 이러한 함수를 순서대로 호출하는 함수를 반환하며, 한 함수의 출력을 다음으로 전달할 때마다 반환합니다. 예를 들면 다음과 같습니다.

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

은 과 동일한 의미임

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

이를 "함수 컴퍼지션"으로 알려져 있으며, 다음과 같은 형식의 신경망을 표현하는 데 특히 편리합니다.

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

현재 모델로 돌아오면 이 식은 Sequential 단순히 모델에 다음과 같은 형식이 있다고 말합니다.

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

작업 1: 일괄 처리 정규화 추가

이제 모델에 새 계층, 특히 일괄 정규화를 추가하려고 합니다.

일괄 처리 정규화는 수렴 속도를 높이기 위한 인기 있는 기술입니다. 이미지 처리 설정(예: 이미지 인식에 대한 다른 실습 랩)에 자주 사용됩니다. 그러나 되풀이 모델에서도 작동할 수 있을까요?

따라서 되풀이 LSTM 계층 전후에 일괄 처리 정규화 계층을 삽입해야 합니다. 이미지 처리에 대한 실습 랩을 완료한 경우 일괄 처리 정규화 계층의 형식은 다음과 같습니다.

BatchNormalizationLayer{}

따라서 계속 진행하여 구성을 수정하고 어떤 일이 일어나는지 확인하세요.

모든 것이 제대로 진행되면 이전 구성에 비해 수렴 속도(ceerrs)가 향상되었을 뿐만 아니라 2.0%(2.9%)의 오류율도 향상됩니다.

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

(학습이 완료될 때까지 기다리지 않으려면 이름 slu.forward.cmf아래에서 결과 모델을 찾을 수 있습니다.)

여기서 솔루션을 참조하세요.

작업 2: Lookahead 추가

되풀이 모델은 구조적 적자를 겪고 있습니다. 되풀이가 왼쪽에서 오른쪽으로 실행되므로 슬롯 레이블에 대한 결정에는 예정된 단어에 대한 정보가 없습니다. 모델은 약간 기울어진 것입니다. 되풀이에 대한 입력이 현재 단어뿐만 아니라 다음 단어(lookahead)로 구성되도록 모델을 수정하는 것입니다.

솔루션은 함수 컴퍼지션 스타일이어야 합니다. 따라서 다음을 수행하는 BrainScript 함수를 작성해야 합니다.

  • 하나의 입력 인수를 허용합니다.
  • 함수를 사용하여 이 입력의 즉각적인 "미래 값"을 FutureValue() 계산합니다(이 특정 형식 사용: FutureValue (0, input, defaultHiddenActivation=0)); 및
  • 를 사용하여 Splice() 두 가지를 포함 차원의 두 배의 벡터에 연결합니다(이 양식 사용: Splice (x:y))

그런 다음 포함 계층과 되풀이 계층 사이에 이 함수 Sequence() 를 삽입합니다. 모두 잘 진행되면 다음 출력이 표시됩니다.

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

이것은 효과가있었다! 다음 단어를 알면 슬롯 태거가 오류율을 2.0%에서 1.84%로 줄일 수 있습니다.

(학습이 완료될 때까지 기다리지 않으려면 이름 slu.forward.lookahead.cmf아래에서 결과 모델을 찾을 수 있습니다.)

여기서 솔루션을 참조하세요.

작업 3: 양방향 되풀이 모델

아하, 미래의 단어에 대한 지식이 도움이 됩니다. 그래서 대신 한 단어 lookahead의, 왜 이전 되풀이를 통해, 문장의 끝까지 모든 방법을 앞을 보지? 양방향 모델을 만들어 보겠습니다.

작업에는 데이터에 대한 정방향 및 역방향 재귀를 모두 수행하고 출력 벡터를 연결하는 새 계층을 구현하는 것입니다.

그러나 양방향 계층에 학습 가능한 모델 매개 변수가 포함되어 있다는 점에서 이전 작업과 다릅니다. 함수 컴퍼지션 스타일에서 모델 매개 변수를 사용하여 레이어를 구현하는 패턴은 함수 개체를 만드는 팩터리 함수를 작성하는 것입니다.

함수 개체( functor라고도 함)는 함수와 개체 모두인 개체입니다. 즉, 데이터가 포함된 다른 것은 함수인 것처럼 여전히 호출할 수 없습니다.

예를 들어 가중치 행렬W, LinearLayer{outDim} 바이어스 b및 계산W * input + b할 다른 함수를 포함하는 함수 개체를 반환하는 팩터리 함수입니다. 예를 들어 말하는 LinearLayer{1024} 것은 다른 함수처럼 사용할 수 있는 이 함수 개체를 즉시 LinearLayer{1024}(x)만듭니다.

혼란? 예를 들어, 선형 계층과 후속 일괄 처리 정규화를 결합하는 새 계층을 구현해 보겠습니다. 함수 컴퍼지션을 허용하려면 다음과 같은 팩터리 함수로 레이어를 실현해야 합니다.

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

이 팩터리 함수를 호출하면 먼저 세 개의 멤버FGapply가 있는 레코드(표시됨{...})가 만들어집니다. 이 예제 FG 에서는 함수 개체 자체이며 apply 데이터에 적용할 함수입니다. 이 식에 추가 .apply 한다는 것은 레코드 멤버에 액세스하기 위해 BrainScript에서 항상 무엇을 .x 의미하는지를 의미합니다. 따라서 예를 들어 호출 LinearLayerWithBN{1024} 은 일괄 처리 정규화 함수 개체라는 F선형 계층 함수 개체를 포함하는 개체 Gapply 만들고, 이를 사용하여 FG이 계층의 실제 연산을 구현하는 함수입니다. 그런 다음 반환 apply됩니다. 바깥쪽에서는 apply() 함수처럼 보이고 동작합니다. 그러나 apply() 내부적으로는 해당 레코드가 속한 레코드를 유지하므로 해당 특정 인스턴스 및 에 대한 액세스 권한을 유지합니다 FG.

이제 다시 손에 우리의 작업으로 돌아갑니다. 이제 위의 예제와 매우 유사하게 팩터리 함수를 만들어야 합니다. 두 개의 되풀이 계층 인스턴스(한 개의 정방향, 한 뒤로)를 만든 다음 두 계층 인스턴스를 동일하게 x 적용하고 두 결과를 연결할 함수를 정의하는 apply (x) 팩터리 함수를 만들어야 합니다.

좋아, 그것을 시도하십시오! CNTK에서 이전 재귀를 실현하는 방법을 알아보려면 정방향 재귀가 수행되는 방식에서 힌트를 받아 보세요. 다음도 수행하세요.

  • 이전 작업에서 추가한 한 단어의 lookahead를 제거합니다. 및
  • 매개 변수를 hiddenDim 300에서 150으로 변경하여 총 모델 매개 변수 수를 제한합니다.

이 모델을 성공적으로 실행하면 다음 출력이 생성됩니다.

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

매력처럼 작동! 이 모델은 위의 lookahead 모델보다 약간 더 나은 1.83%를 달성합니다. 양방향 모델에는 lookahead 모델보다 매개 변수가 40% 적습니다. 그러나 돌아가서 전체 로그 출력(이 웹 페이지에 표시되지 않음)을 자세히 살펴보면 lookahead가 약 30% 더 빠르게 학습된 것을 확인할 수 있습니다. 이는 lookahead 모델에 가로 종속성이 적고(두 되풀이가 아닌 하나) 행렬 제품이 많기 때문에 더 높은 병렬 처리를 달성할 수 있기 때문입니다.

여기서 솔루션을 참조하세요.

작업 4: 의도 분류

지금까지 생성한 모델을 의도 분류자로 쉽게 전환할 수 있습니다. 데이터 파일에는 이 추가 열 S1이 포함되어 있습니다. 이 열에는 문장당 단일 레이블이 포함되어 있으며, 이는 쿼리가 토픽과 같은 airport 정보를 찾으려고 했음 airfare을 나타냅니다.

전체 시퀀스를 단일 레이블로 분류하는 작업을 시퀀스 분류라고 합니다. 시퀀스 분류자는 마지막 단계의 숨겨진 상태를 취하는 되풀이 LSTM(이미 포함)으로 구현됩니다. 이렇게 하면 각 시퀀스에 대한 단일 벡터가 표시됩니다. 이 벡터는 소프트맥스 분류를 위해 조밀한 계층으로 공급됩니다.

CNTK에는 시퀀스에서 마지막 상태를 추출하는 작업이 있습니다 BS.Sequences.Last(). 이 작업은 동일한 미니배치에 길이가 매우 다른 시퀀스를 포함할 수 있으며 압축된 형식으로 메모리에 정렬된다는 사실을 적용합니다. 마찬가지로 이전 재귀의 BS.Sequences.First()경우 .

작업은 마지막 프레임이 정방향 재귀에서 추출되고 첫 번째 프레임이 역방향 재귀에서 추출되고 두 벡터가 연결되도록 작업 3에서 양방향 네트워크를 수정하는 것입니다. 연결된 벡터(생각 벡터라고도 함)는 조밀한 계층의 입력이어야 합니다.

또한 슬롯에서 의도 레이블로 레이블을 변경해야 합니다. 대신 의도 레이블에 대한 판독기 섹션에서 사용되는 이름과 일치하도록 입력 변수(slotLabels)의 이름을 바꾸고 차원과 일치합니다.

수정해 보세요. 그러나 올바르게 수행하면 짜증 나는 오류 메시지와 긴 오류 메시지에 직면하게 됩니다.

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?

"다른 시퀀스 길이를 사용하고 있나요?" 네! 쿼리 및 의도 레이블 do-- 의도 레이블은 쿼리당 하나의 토큰에 불과합니다. 1 요소의 시퀀스입니다! 그렇다면 이 문제를 해결하는 방법은 무엇일까요?

CNTK를 사용하면 네트워크의 다른 변수가 서로 다른 시퀀스 길이를 가질 수 있습니다. 시퀀스 길이를 기호화된 추가 텐서 차원으로 생각할 수 있습니다. 동일한 길이의 변수는 동일한 기호 길이 차원을 공유합니다. 두 변수의 길이가 서로 다르면 명시적으로 선언해야 합니다. 그렇지 않으면 CNTK는 모든 변수가 동일한 기호 길이를 공유한다고 가정합니다.

이 작업은 다음과 같이 새 동적 축 개체를 만들고 입력 중 하나에 연결하여 수행됩니다.

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

CNTK에는 기본 축이 있습니다. 위의 예외에서 짐작할 수 있듯이 해당 이름은 '*'입니다.
따라서 하나의 새 축만 선언하면 됩니다. 다른 입력(intentLabels)은 기본 축을 계속 사용합니다.

이제 실행하는 것이 좋으며 다음 출력을 참조하세요.

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

많은 노력 없이 4.1%의 오류율을 달성했습니다. 첫 번째 샷에 대 한 아주 좋은 (비록이 작업에 예술의 꽤 상태, 3%에.

Epoch당 샘플 수는 현재 약 2700개입니다. 이는 현재 문장당 하나만 있는 레이블 샘플의 수이기 때문입니다. 이 작업에는 감독 신호 수가 크게 감소했습니다. 이렇게 하면 미니배치 크기를 늘리도록 권장됩니다. 70 대신 256을 사용해 보겠습니다.

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

이 시스템은 훨씬 더 잘 배웁니다! 그러나 이러한 차이는 그라데이션 정규화 체계로 인한 fsAdagrad 아티팩트일 수 있으며 일반적으로 더 큰 데이터 집합을 사용할 때 곧 사라집니다.

하지만 결과 오류 비율은 더 높습니다.

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

그러나 이 차이는 실제로 3개의 오류에 해당하며 이는 중요하지 않습니다.

해결 방법은 여기를 참조하세요.

작업 5: 병렬 학습

마지막으로, 여러 GPU가 있는 경우 CNTK를 사용하면 MPI(메시지 전달 인터페이스)를 사용하여 학습을 병렬화할 수 있습니다. 이 모델은 너무 작아서 속도를 기대할 수 없습니다. 이러한 작은 모델을 병렬화하면 사용 가능한 GPU가 심각하게 미달됩니다. 그럼에도 불구하고 실제 워크로드로 이동한 후 작업을 수행하는 방법을 알 수 있도록 동작을 살펴보겠습니다.

블록에 다음 줄을 SGD 추가하세요.

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

다음 명령을 실행합니다.

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

이렇게 하면 1비트 SGD 알고리즘(이 경우 실제로 2비트 SGD)을 사용하여 4개의 GPU에서 학습을 실행합니다. 해당 근사값은 정확도를 저하시키지 않았습니다. 오류 비율은 4.367%이며, 두 번의 오류가 더 발생합니다(단일 GPU에서 별도로 작업을 실행 TestTagger 하세요).

결론

이 자습서에서는 네트워크를 나타내는 컴팩트한 수단으로 함수 컴퍼지션 스타일을 도입했습니다. 많은 신경망 유형은 그래프를 네트워크 설명으로 보다 직접적이고 오류가 발생하기 쉬운 변환인 이러한 방식으로 나타내는 데 적합합니다.

이 자습서에서는 함수 컴퍼지션 스타일의 기존 구성을 사용하고 특정 방식으로 수정하는 방법을 연습했습니다.

  • 레이어 추가(미리 정의된 레이어 갤러리에서)
  • 함수 정의 및 사용
  • 레이어 팩터리 함수 정의 및 사용

이 자습서에서는 여러 시간 차원의 처리에 대해서도 설명했으며 학습을 병렬화하는 방법을 알아보았습니다.

솔루션

위의 작업에 대한 해결 방법은 다음과 같습니다. 이봐, 아니 부정 행위!

해결 방법 1: Batch 정규화 추가

수정된 모델 함수의 형식은 다음과 같습니다.

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

해결 방법 2: Lookahead 추가

lookahead-function은 다음과 같이 정의될 수 있습니다.

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

다음과 같이 모델에 삽입됩니다.

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

해결 방법 3: 양방향 되풀이 모델

양방향 되풀이 계층은 다음과 같이 작성될 수 있습니다.

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

그런 다음 다음과 같이 사용됩니다.

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

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

솔루션 4: 의도 분류

시퀀스를 되풀이 계층의 마지막/첫 번째 숨김으로 줄입니다.

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

레이블 입력을 슬롯에서 의도로 변경합니다.

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

새 동적 축을 사용합니다.

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

승인

이 자습서의 기초를 준비해 주신 데릭 리우에게 감사드립니다.