Main Content

이 번역 페이지는 최신 내용을 담고 있지 않습니다. 최신 내용을 영문으로 보려면 여기를 클릭하십시오.

텍스트 디코더 모델 함수 정의하기

이 예제에서는 텍스트 디코더 모델 함수를 정의하는 방법을 보여줍니다.

딥러닝의 문맥에서 디코더란 잠재 벡터를 어떤 샘플 공간에 매핑하는 딥러닝 신경망을 이루는 한 부분을 말합니다. 벡터를 디코딩하여 다양한 작업에 사용할 수 있습니다. 예를 들면 다음과 같습니다.

  • 순환 신경망을 인코딩된 벡터로 초기화하여 텍스트 생성.

  • 인코딩된 벡터를 문맥 벡터로 사용하여 sequence-to-sequence 변환.

  • 인코딩된 벡터를 문맥 벡터로 사용하여 이미지 캡션 생성.

데이터 불러오기

sonnetsEncoded.mat에서 인코딩된 데이터를 불러옵니다. 이 MAT 파일에는 예제 Define Text Encoder Model Function (Deep Learning Toolbox)에서 사용된 단어 인코딩, 시퀀스 미니 배치 X, 그리고 이에 대응하여 인코더로 출력된 인코딩 데이터 Z가 포함되어 있습니다.

s = load("sonnetsEncoded.mat");
enc = s.enc;
X = s.X;
Z = s.Z;

[latentDimension,miniBatchSize] = size(Z,1:2);

모델 파라미터 초기화하기

디코더의 목표는 어떤 초기 입력 데이터와 신경망 상태가 주어지면 시퀀스를 생성하는 것입니다.

다음 모델의 파라미터를 초기화합니다.

디코더는 인코더 출력값으로 초기화된 LSTM을 사용하여 입력값을 재구성합니다. 각 시간 스텝에 대해 디코더는 다음 시간 스텝을 예측하고, 그 출력값을 다음 시간 스텝 예측에 사용합니다. 인코더와 디코더는 모두 동일한 임베딩을 사용합니다.

이 모델에는 다음과 같은 세 가지 연산이 사용됩니다.

  • 임베딩은 범위 1~vocabularySize 내에 있는 단어 인덱스를 차원 embeddingDimension으로 구성된 벡터에 매핑합니다. 여기서 vocabularySize는 인코딩 단어집에 있는 단어 개수이고, embeddingDimension은 임베딩으로 학습된 성분 개수입니다.

  • LSTM 연산은 단일 단어 벡터를 입력값으로 받고 1×numHiddenUnits 벡터를 출력합니다. 여기서 numHiddenUnits는 LSTM 연산의 은닉 유닛 개수입니다. LSTM 신경망의 초기 상태(첫 번째 시간 스텝에서의 상태)는 인코딩된 벡터이기 때문에 은닉 유닛의 개수는 인코더의 잠재 차원과 일치해야 합니다.

  • 완전 연결 연산은 편향을 더하는 가중치 행렬에 입력값을 곱하여 vocabularySize 크기의 벡터를 출력합니다.

파라미터 차원을 지정합니다. 임베딩 크기는 인코더와 일치해야 합니다.

embeddingDimension = 100;
vocabularySize = enc.NumWords;
numHiddenUnits = latentDimension;

파라미터 구조체를 만듭니다.

parameters = struct;

initializeGaussian 함수를 사용해 가우스를 사용하여 임베딩의 가중치를 초기화합니다. 이 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 평균은 0으로, 표준편차는 0.01로 지정합니다. 자세한 내용은 Gaussian Initialization (Deep Learning Toolbox) 항목을 참조하십시오.

sz = [embeddingDimension vocabularySize];
mu = 0;
sigma = 0.01;
parameters.emb.Weights = initializeGaussian(sz,mu,sigma);

디코더 LSTM 연산에 대해 학습 가능한 파라미터를 다음과 같이 초기화합니다.

  • initializeGlorot 함수를 사용해 Glorot 이니셜라이저를 사용하여 입력 가중치를 초기화합니다. 이 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 자세한 내용은 Glorot Initialization (Deep Learning Toolbox) 항목을 참조하십시오.

  • initializeOrthogonal 함수를 사용해 직교 이니셜라이저를 사용하여 순환 가중치를 초기화합니다. 이 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 자세한 내용은 Orthogonal Initialization (Deep Learning Toolbox) 항목을 참조하십시오.

  • initializeUnitForgetGate 함수를 사용해 단위 망각 게이트 이니셜라이저를 사용하여 편향을 초기화합니다. 이 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 자세한 내용은 Unit Forget Gate Initialization (Deep Learning Toolbox) 항목을 참조하십시오.

학습 가능한 파라미터의 크기는 입력값의 크기에 따라 다릅니다. LSTM 연산에 대한 입력값이 임베딩 연산에서 얻은 단어 벡터의 시퀀스이기 때문에 입력 채널의 개수는 embeddingDimension입니다.

  • 입력 가중치 행렬의 크기는 4*numHiddenUnits×inputSize입니다. 여기서 inputSize는 입력 데이터의 차원입니다.

  • 순환 가중치 행렬의 크기는 4*numHiddenUnits×numHiddenUnits입니다.

  • 편향 벡터의 크기는 4*numHiddenUnits×1입니다.

sz = [4*numHiddenUnits embeddingDimension];
numOut = 4*numHiddenUnits;
numIn = embeddingDimension;

parameters.lstmDecoder.InputWeights = initializeGlorot(sz,numOut,numIn);
parameters.lstmDecoder.RecurrentWeights = initializeOrthogonal([4*numHiddenUnits numHiddenUnits]);
parameters.lstmDecoder.Bias = initializeUnitForgetGate(numHiddenUnits);

인코더가 완전히 연결된 연산에 대해 학습 가능한 파라미터를 다음과 같이 초기화합니다.

  • Glorot 이니셜라이저를 사용하여 가중치를 초기화합니다.

  • initializeZeros 함수를 사용하여 편향을 0으로 초기화합니다. 이 함수는 이 예제에 지원 파일로 첨부되어 있습니다. 자세한 내용은 Zeros Initialization (Deep Learning Toolbox) 항목을 참조하십시오.

학습 가능한 파라미터의 크기는 입력값의 크기에 따라 다릅니다. 완전 연결 연산에 대한 입력값이 LSTM 연산의 출력값이기 때문에 입력 채널의 개수는 numHiddenUnits입니다. 완전 연결 연산에서 latentDimension 크기의 벡터를 출력하기 위해 출력 크기를 latentDimension으로 지정합니다.

  • 가중치 행렬의 크기는 outputSize×inputSize입니다. 여기서 outputSizeinputSize는 각각 출력 차원과 입력 차원에 해당합니다.

  • 편향 벡터의 크기는 outputSize×1입니다.

완전 연결 연산에서 vocabularySize 크기의 벡터를 출력하기 위해 출력 크기를 vocabularySize로 지정합니다.

sz = [vocabularySize numHiddenUnits];
mu = 0;
sigma = 1;
parameters.fcDecoder.Weights = initializeGaussian(sz,mu,sigma);
parameters.fcDecoder.Bias = initializeZeros([vocabularySize 1]);

모델 디코더 함수 정의하기

디코더 모델의 출력값을 계산하는 함수 modelDecoder(이 예제의 디코더 모델 함수 섹션에 나와 있음)를 만듭니다. modelDecoder 함수는 단어 인덱스의 시퀀스와 모델 파라미터, 시퀀스 길이를 입력값으로 받고 그에 대응하는 잠재 특징 벡터를 반환합니다.

모델 손실 함수에서 모델 함수 사용하기

사용자 지정 훈련 루프를 사용하여 딥러닝 모델을 훈련시킬 경우, 학습 가능한 파라미터에 대한 손실과 손실의 기울기를 계산해야 합니다. 이 계산은 모델 함수의 순방향 통과 출력값에 따라 달라집니다.

다음은 디코더로 텍스트 데이터를 생성하는 일반적인 두 가지 방법입니다.

  1. 폐루프 — 각 시간 스텝에 대해 이전 예측값을 입력값으로 사용하여 예측을 수행합니다.

  2. 개루프 — 각 시간 스텝에 대해 외부 소스(예: 훈련 목표값)의 입력값을 사용하여 예측을 수행합니다.

폐루프 생성

폐루프 생성은 모델이 데이터를 한 번에 하나의 시간 스텝씩 생성한 후 이전 예측값을 다음 예측의 입력값으로 사용하는 경우를 말합니다. 개루프 생성과 달리 이 과정에서는 예측 간에 입력값이 필요하지 않기 때문에 비지도 시나리오에 최적입니다. 예를 들어 출력 텍스트를 한꺼번에 생성하는 언어 변환 모델이 이에 해당할 수 있습니다.

LSTM 신경망의 은닉 상태를 인코더 출력값 Z로 초기화합니다.

state = struct;
state.HiddenState = Z;
state.CellState = zeros(size(Z),'like',Z);

첫 번째 시간 스텝의 경우 시작 토큰으로 구성된 배열을 디코더의 입력값으로 사용합니다. 문제를 단순화하기 위해, 훈련 데이터의 첫 번째 시간 스텝에서 시작 토큰으로 구성된 배열을 추출합니다.

decoderInput = X(:,:,1);

디코더 출력값을 크기 numClasses×miniBatchSize×sequenceLengthdlX와 동일한 데이터형을 갖도록 사전할당합니다. 여기서 sequenceLength는 이 생성에서 원하는 길이(예: 훈련 목표값의 길이)입니다. 이 예제에서는 시퀀스 길이를 16으로 지정합니다.

sequenceLength = 16;
Y = zeros(vocabularySize,miniBatchSize,sequenceLength,"like",X);
Y = dlarray(Y,"CBT");

각 시간 스텝에 대해 modelDecoder 함수를 사용하여 시퀀스의 다음 시간 스텝을 예측합니다. 예측이 끝날 때마다 디코더 출력값의 최댓값에 해당하는 인덱스를 찾고 그 인덱스를 다음 시간 스텝의 디코더 입력값으로 사용합니다.

for t = 1:sequenceLength
    [Y(:,:,t), state] = modelDecoder(parameters,decoderInput,state);
    
    [~,idx] = max(Y(:,:,t));
    decoderInput = idx;
end

출력값은 vocabularySize×miniBatchSize×sequenceLength 배열입니다.

size(Y)
ans = 1×3

        3595          32          16

이 코드 조각은 모델 기울기 함수에서 폐루프 생성을 수행하는 예제를 보여줍니다.

function [loss,gradients] = modelLoss(parameters,X,sequenceLengths)

    % Encode input.
    Z = modelEncoder(parameters,X,sequenceLengths);

    % Initialize LSTM state.
    state = struct;
    state.HiddenState = Z;
    state.CellState = zeros(size(Z),"like",Z);

    % Initialize decoder input.
    decoderInput = X(:,:,1);

    % Closed loop prediction.
    sequenceLength = size(X,3);
    Y = zeros(numClasses,miniBatchSize,sequenceLength,"like",X);
    for t = 1:sequenceLength
        [Y(:,:,t), state] = modelDecoder(parameters,decoderInput,state);
    
        [~,idx] = max(Y(:,:,t));
        decoderInput = idx;
    end

    % Calculate loss.
    % ...

    % Calculate gradients.
    % ...

end

개루프 생성: 교사 강요(Teacher Forcing)

폐루프 생성을 사용하여 훈련시킬 경우 시퀀스의 각 스텝에 대해 가장 가능성 높은 단어를 예측할 때 최적이 아닌 결과가 생성될 수 있습니다. 예를 들어, 이미지 캡션 생성 워크플로에서 코끼리 이미지가 주어졌을 때 디코더가 캡션의 첫 번째 단어를 "a"로 예측하면, 그다음 단어로 "elephant"를 예측할 가능성이 희박합니다. 영어 텍스트에서 "a elephant"라는 구절이 나타날 가능성이 거의 없기 때문입니다.

신경망 수렴 속도를 빠르게 하기 위해 교사 강요(teacher forcing)를 사용할 수 있습니다. 이는 이전 예측값을 사용하는 대신 목표값을 디코더에 대한 입력값으로 사용하는 기법입니다. 교사 강요를 사용하면 신경망이 시퀀스의 이후 시간 스텝에서 특성을 학습할 수 있습니다. 신경망이 시퀀스의 이전 시간 스텝을 올바로 생성할 때까지 기다릴 필요가 없습니다.

교사 강요를 수행하려면 목표 시퀀스를 입력값으로 하여 modelEncoder 함수를 직접 사용하십시오.

LSTM 신경망의 은닉 상태를 인코더 출력값 Z로 초기화합니다.

state = struct;
state.HiddenState = Z;
state.CellState = zeros(size(Z),"like",Z);

목표 시퀀스를 입력값으로 사용하여 예측을 수행합니다.

Y = modelDecoder(parameters,X,state);

출력값은 vocabularySize×miniBatchSize×sequenceLength 배열입니다. 여기서 sequenceLength는 입력 시퀀스의 길이입니다.

size(Y)
ans = 1×3

        3595          32          14

이 코드 조각은 모델 기울기 함수에서 교사 강요를 수행하는 예제를 보여줍니다.

function [loss,gradients] = modelLoss(parameters,X,sequenceLengths)

    % Encode input.
    Z = modelEncoder(parameters,X);

    % Initialize LSTM state.
    state = struct;
    state.HiddenState = Z;
    state.CellState = zeros(size(Z),"like",Z);

    % Teacher forcing.
    Y = modelDecoder(parameters,X,state);

    % Calculate loss.
    % ...

    % Calculate gradients.
    % ...

end

디코더 모델 함수

modelDecoder 함수는 모델 파라미터, 단어 인덱스의 시퀀스와 신경망 상태를 입력값으로 받고 디코딩된 시퀀스를 반환합니다.

lstm 함수가 상태 유지 함수(시계열이 입력값으로 지정되면 이 함수는 각 시간 스텝 간에 상태를 전파하고 업데이트함)이고 기본적으로 embed 함수와 fullyconnect 함수가 시간 분산 함수(시계열이 입력값으로 지정되면 이러한 함수는 각 시간 스텝에 대해 독립적으로 연산을 수행함)이기 때문에 modelDecoder 함수는 시퀀스와 단일 시간 스텝 입력값을 모두 지원합니다.

function [Y,state] = modelDecoder(parameters,X,state)

% Embedding.
weights = parameters.emb.Weights;
X = embed(X,weights);

% LSTM.
inputWeights = parameters.lstmDecoder.InputWeights;
recurrentWeights = parameters.lstmDecoder.RecurrentWeights;
bias = parameters.lstmDecoder.Bias;

hiddenState = state.HiddenState;
cellState = state.CellState;

[Y,hiddenState,cellState] = lstm(X,hiddenState,cellState, ...
    inputWeights,recurrentWeights,bias);

state.HiddenState = hiddenState;
state.CellState = cellState;

% Fully connect. 
weights = parameters.fcDecoder.Weights;
bias = parameters.fcDecoder.Bias;
Y = fullyconnect(Y,weights,bias);

end

참고 항목

| | |

관련 항목