2019년 7월 13일 토요일

음성인식 메모(kaldi) 9 - pre-training Karel's DNN

「原作者へ」

連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。 
正式に翻訳を許可されたいです。 
gogyzzz@gmail.comでご連絡ください。

아래 포스트의 번역입니다.

http://work-in-progress.hatenablog.com/entry/2018/04/09/211753


pre-training의 흐름을 따라가보자.

input이 되는 음성은 연속값이므로, GB(Gaussian-Bernoulli)형의 Restricted boltzmann machine(RBM)을 사용한다.

  • visible: Gaussian, 연속값을 취한다.
  • hidden: Bernoulli, 이산값을 취한다.

nnet (Karel's DNN)에서는 「nnetbin/rbm-train-cd1-frmshuff」 커맨드를 실행한다.

nnetbin/rbm-train-cd1-frmshuff 1.rbm.init ark:mosimosi.ark 1.rbm

「1.rbm.init」은 「nnetbin/nnet-initialize」 커맨드에 아래 prototype을 input으로 전달하여 생성한다.

<NnetProto>
<Rbm> <InputDim> 13 <OutputDim> 16 <VisibleType> gauss <HiddenType> bern <ParamStddev> 0.1
</NnetProto>

아래 그림은 visible을 「13」, hidden layer를 「16」으로 나타낸 것

데이터를 저장하는 행렬은 소스 내의 변수이름으로 표시되어 있다.

mini : mini-batch size
( ) x ( )은 행렬을 나타낸다(row x col)

일단 forward-pass를 구한다.

pos_vis

feature로부터 미니 배치 파일만큼을(여기 예시에서는 디폴트 값 100) 취하여 프레임 순서를 랜덤으로 섞는다

vis_hid

「1.rbm.init」, hidden layer의 파라미터

pos_hid

「posvis」와 「vishid」(Transpose)의 곱셈

pos_hid(sigmoid 적용 후)

이것에 랜덤값을 더하여 [-1..1]의 범위가 되도록 조절한다.

또한 Heaviside step function을 적용한다(0보다 크다면 「1」、0보다 작으면 「0」)

poshidaux

이것에 「vishid」(hidden layer 파라미터 16row x 13col)을 곱한 것이 「negvis」이 된다.

neg_vis

「posvis」와 같은 방식으로 「negvis」에 대해 forward-pass을 구한다.

neg_hid(sigmoid 적용 후)

여기까지 얻은 4개의 행렬 「posvis」、「negvis」、「poshid」、「neghid」을 가지고 hidden layer 파라미터 「vis_hid」를 업데이트 한다.

일단 「neghid」(100row x 16col, Transpose)와 「negvis」(100row x 13col)를 곱한다.

이것에 「poshid」(100row x 16col, Transpose)와 「posvis(100row x 13col)」를 곱한 것을 더한다.

더한 결과

이어서 「vis_hid」에 learning-rate와 L2 regularization 파라미터를 곱한 것을 더한다.

이 결과를 「vis_hid」에 더한 것이 업데이트 후의 파라미터가 된다.

vis_hid

음성인식 메모(kaldi) 8 - training Karel's DNN

「原作者へ」

連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。 
正式に翻訳を許可されたいです。 
gogyzzz@gmail.comでご連絡ください。

아래 포스트의 번역입니다.

http://work-in-progress.hatenablog.com/entry/2018/04/02/214421


DNN모델의 학습 과정을 따라가보자.

학습에는 「nnetbin/nnet-train-frmshuff」 커맨드를 사용한다.

nnetbin/nnet-train-frmshuff \
--minibatch-size=128 \
--randomize=false \
scp:train/feats.scp \          <--input이 되는 feature
ark:mosimosi_ali2post.ark \    <-- 정답 파일
nnet.init \                    <-- 모델
dbn_dnn_iter1                  <-- 업데이트 후의 모델

옵션으로 아래와 같이 지정. 처리의 흐름을 파악하는 것이 목적이므로 randomize는 off로 한다.

mini-batch의 사이즈는 「128」(=2의 7승, 198프레임에 대한 coverage는 65%)

randomize은 사용하지 않는다(input이 되는 MFCC의 프레임을 shuffle하지 않는다)

초기 모델(nnet.init)은 아래의 조건으로 생성

  • input layer : 13 (MFCC13차원)
  • output layer : 6 (pdf-id의 수)
  • hidden layer(중간층)의 수 : 1
  • hidden layer의 neuron 수 : 256

Propagate(Forward)의 흐름은 아래 그림과 같다.

그림의 의미

  • mini : mini-batch size(여기 예시에서는 「128」)
  • prop_buf : forward-pass buffers
  • transpose : transpose 행렬을 사용

그림의 "prop_buf[5]"가 output layer에 있어서의 activation(softmax)의 출력이 된다.

값은 아래와 같이 나타났다. 각 프레임에서 가장 확률이 높은 것을 녹색, 정답에 해당하는 것은 빨간색으로 표시하였다.

한편 정답이 되는 Matrix는 아래 그림과 같다.

이것은 "bin/ali-to-post" 커맨드의 출력 결과인 "mosimosi_ali2post.ark"에서 생성된 것

mosimosi_ali2post.ark

utterance_id_001 [ 3 1 ] [ 3 1 ] [ 3 1 ] [ 3 1 ] [ 3 1 ]  ...(이후 193개 생략)

2개의 Matrix의 차를 얻자

이것이 back propagation의 input이 된다.

Propagate(backward)의 흐름은 아래 그림과 같다(2개의 Matrix의 차를 구한 것은 "backprop_buf[5]"에 해당)

그림의 의미

  • backprop_buf : backward-pass buffers

모델의 파라미터 업데이트에 사용되는 gradient를 구해보자.

그림의 의미

linearitycorr : gradient

gradient

이것에 learning rate 「0.008」을 곱한 것을 업데이트 전의 파라미터에 빼서 파라미터를 업데이트한다.

파라미터 (업데이트 전)

파라미터(업데이트 후)

음성인식 메모(kaldi) 7 - decode with Karel's DNN

「原作者へ」

連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。 
正式に翻訳を許可されたいです。 
gogyzzz@gmail.comでご連絡ください。

아래 포스트의 번역입니다.

http://work-in-progress.hatenablog.com/entry/2018/03/29/124545


Deep Neural Network Features를 사용한 decode를 알아보자.

Kaldi 공식 사이트 Deep Neural Networks in Kaldi에 의하면 3종류가 있는 것 같다.

nnet(Karel씨가 쓴것) nnet2(Dan씨) nnet3(Dan씨)

이번엔 Karel씨가 쓴 버전을 알아보자.

결과 먼저 보자.

실행 커맨드

bin/decode-faster-mapped \
--word-symbol-table=lang/words.txt \
tri/final.mdl \
HCLG.fst \
ark:mosimosi_loglikes.ark \
ark,t:-

GMM-HMM을 사용한 decode에서는 feature vector 파일(13次元、198프레임)을 건넸던 부분이 DNN을 거친 (6차원, 198프레임)으로 바뀌어 있다.

출력 결과

utterance_id_001 2 
utterance_id_001 MOSIMOSI 
LOG (decode-faster-mapped[5.3.106~1389-9e2d8]:main():decode-faster-mapped.cc:143) Log-like per frame for utterance utterance_id_001 is 0.67109 over 198 frames.
LOG (decode-faster-mapped[5.3.106~1389-9e2d8]:main():decode-faster-mapped.cc:155) Time taken [excluding initialization] 0.0379519s: real-time factor assuming 100 frames/sec is 0.0191676
LOG (decode-faster-mapped[5.3.106~1389-9e2d8]:main():decode-faster-mapped.cc:158) Done 1 utterances, failed for 0
LOG (decode-faster-mapped[5.3.106~1389-9e2d8]:main():decode-faster-mapped.cc:160) Overall log-likelihood per frame is 0.67109 over 198 frames.

mosimosi_loglikes.ark

utterance_id_001  [
  0.1099651 0.09116774 0.081482 0.04679191 0.01571052 0.6548827 
  0.1098814 0.09139811 0.08160141 0.0467133 0.01567704 0.6547288 
  0.1108724 0.09039295 0.0817743 0.04703647 0.0159712 0.6539527 
  (중도 생략, 194개 프레임)
  0.1097786 0.09063407 0.08120951 0.04691367 0.01570774 0.6557564 ]

각 프레임의 column 수는 「6」(GMM-HMM에 있어서 pdf수는 「6」)、row를 덧붙이면 1이 된다.

지금은 흐름을 파악하는 것이 목적이므로, 학습을 생략한 초기 상태의 Neural Network를 전달한다.

input이 되는 파일을 생성하는 커맨드는 다음과 같다.

nnetbin/nnet-forward \
--feature-transform=final.feature_transform \
nnet_dbn_dnn.init \
ark:mosimosi.ark \
ark:mosimosi_loglikes.ark

final.feature_transform

<Nnet> 
<Splice> 143 13 
[ -5 -4 -3 -2 -1 0 1 2 3 4 5 ]
<!EndOfComponent> 
<AddShift> 143 143 
<LearnRateCoef> 0  [ -65.69167 9.436182 10.43176 -2.286287 2.41689 10.67416 -4.945405 0.9329144 5.600904 -9.443085 8.796977 -5.058812 0.7505272 (이후 130개는 생략) ]
<!EndOfComponent> 
<Rescale> 143 143 
<LearnRateCoef> 0  [ 0.05615381 0.05059185 0.07485399 0.1185114 0.09485025 0.07829515 0.07323452 0.09640685 0.08708434 0.07876774 0.09747799 0.1328274 0.1090247 (이후 130개는 생략)]
<!EndOfComponent> 
</Nnet> 

nnetdbndnn.init

<Nnet> 
<AffineTransform> 2048 143 
<LearnRateCoef> 1 <BiasLearnRateCoef> 1 <MaxNorm> 0 
 [
0.02285465 -0.005800713 -0.03342053 0.0009499519 0.001076195 0.001254448 -0.005117053 -0.005607297 -0.005614388 -0.003707627 0.03109564 -0.01724246 -0.006429902 0.0242731 (이후 130개는 생략)
...(이후 2047행 생략) 
 ] <-- (2048행 x 143열)
 [ -9.79805e-05 -0.0001669982 -0.0001459182 -0.0001764622 -0.0001836212 -0.0001546516 -0.0001855577 -9.09675e-05 -0.0001460401 -0.0002300229 -0.0002415479 -0.0001545625 -9.22261e-05(이후 2035개는 생략) ] <-- (1행 x 2048열)
<!EndOfComponent> 
<Sigmoid> 2048 2048 
<!EndOfComponent> 
...(중도 생략、<AffineTransform>、<Sigmoid>의 반복)
<AffineTransform> 6 2048 
<LearnRateCoef> 1 <BiasLearnRateCoef> 1 <MaxNorm> 0 
 [
 (중도 생략) 
 ] <-- (6행 x 2048열)
 [ 0 0 0 0 0 0 ] <-- (1행 x 6열)
<!EndOfComponent> 
<Softmax> 6 6 
<!EndOfComponent> 
</Nnet> 

MFCC에 대해 아래의 순서로 feature transform을 수행한다.

  • Splice
  • AddShift
  • Rescale

여기서, MFCC를 1프레임마다 13차원, splice를 5로 하면 Splice의 결과는 1프레임마다 143차원이 된다.

( 2 * splice + 1) * feat_dim
= ( 2 * 5 + 1) *13
= 11 * 13
= 143

splice는 앞뒤 프레임의 차원을 이어붙인 것

Shift은 더하기, ReScale은 곱하기를 수행한다. (둘 다 143차원으로 실행)

위 처리가 소스 코드로는 아래 부분에서 수행된다.

      // fwd-pass, feature transform,
      nnet_transf.Feedforward(feats, &feats_transf);

변환 결과

(gdb) p feats_transf
$334 = {<kaldi::CuMatrixBase<float>> = {data_ = 0x8291440, num_cols_ = 143, num_rows_ = 198, stride_ = 144}, <No data fields>}

변환 후의 데이터

-0.181488395, 0.827578247, 0.543785274, 0.0556436256, -0.128215984, 0.213598549, 0.592510521, 0.838311195, 0.0181505159, -0.334542274, -1.33981669, -0.902492762, -1.08390939,(以降130個省略)
...(이후 197행 생략)

이어서, 선형 변환(AffineTransform) activation (Sigmoid function)을 적용한다.

최초 Affine 변환으로 143차원에서 2048차원으로 변환된다.

    Nnet nnet;
    nnet.Read(model_filename);
    (중도 생략)
    // fwd-pass, nnet,
    nnet.Feedforward(feats_transf, &nnet_out);

1회째의 AffineTransform(matrix/kaldi-matrix.cc)

    cblas_Xgemm(alpha,       // 1
                transA,      // kaldi::kNoTrans
                A.data_,     // Matrix A(198rows x 143cols)
                A.num_rows_, // 198 rows
                A.num_cols_, // 143 cols
                A.stride_,   // 144
                transB,      // kaldi::kTrans
                B.data_,     // MatrixB(2048rows x 143cols)
                B.stride_,   // 144
                beta,        // 1
                data_,       // MatrixC(198rows x 2048cols)
                num_rows_,   // 198
                num_cols_,   // 2048
                stride_      // 2048
    ); 

내부에서 cblas_dgemm를 호출하고 있다.

matrix/cblas-wrappers.h

// C := alpha * AB + beta * C 
cblas_dgemm(CblasRowMajor,
            static_cast<CBLAS_TRANSPOSE>(transA), // No-Transpose
            static_cast<CBLAS_TRANSPOSE>(transB), // Transpose
            num_rows,                             // m (MatrixA rows) --> 198
            num_cols,                             // n (MatrixB cols) --> 144
            a_num_cols,                           // k (MatA cols, MatB rows) -->2048
            alpha,                                // alpha --> 1
            Adata,                                // Matrix A --> (198rows x 144cols)
            a_stride,                             // k (MatA cols, MatB rows) -->2048
            Bdata,                                // Matrix B --> (2048rows x 144cols)
            b_stride,                             // n (MatrixB cols) --> 144
            beta,                                 // beta --> 1
            Cdata,                                // Matrix C --> (198rows x 2048cols)
            stride                                // n (MatB(transpose) cols) --> 2048
            ); 

결과는 198행x2048열의 행렬이 된다.

$358 = {data_ = 0xb6ef4020, num_cols_ = 2048, num_rows_ = 198, stride_ = 2048}

이에 대해 activation function, 실제 소스에서는 표준 sigmoid 함수(y=1/(1+e^(-x)))를 적용한다.

아래 그림은 프레임 1에 대해 앞 50개의 input과 output을 플롯한 것.

이어서, 선형변환, activation 적용을 몇번 반복하여 마지막 Affine변환을 통해 2048차원에서 6차원으로 변환한다.

그 후 activation으로 softmax를 적용한다.

matrix/kaldi-vector.cc

template<typename Real>
Real VectorBase<Real>::ApplySoftMax() {
    Real max = this->Max(), sum = 0.0;

    // data_ = 
    // {1.58165777, 1.39419591, 1.28187716, 0.727205336, -0.364174455, 3.36595106}

    for (MatrixIndexT i = 0; i < dim_; i++) {
        sum += (data_[i] = Exp(data_[i] - max));
    }

    // data_ = {0.167915687, 0.13921231, 0.124422282, 0.0714508295, 0.0239898264, 1}
    // sum = 1.52699089

    this->Scale(1.0 / sum);  // => 0.6548827544085741

    // max = 3.36595106
    // Log(sum) = 0.423299074
    // max + Log(sum) = 3.78925014
    return max + Log(sum); 
}

data에 대해 Scale을 곱한 것이 출력 결과(mosimosiloglikes.ark)가 된다.

음성인식 메모(kaldi) 6 - gmm training 3

「原作者へ」

連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。 
正式に翻訳を許可されたいです。 
gogyzzz@gmail.comでご連絡ください。

다음 포스트의 번역입니다.

http://work-in-progress.hatenablog.com/entry/2018/03/26/205433


Monophone 모델을 가지고 Triphone 모델을 만들어보자.

일단은 통계량 생성부터

bin/acc-tree-stats --ci-phones=1:2:3:4:5:6:7:8:9:10 2.mdl ark:mosimosi.ark ark:1.ali treeacc

phone index 1〜5은 「sil」、6〜10은 「spn(spoken noise)」

이어서, Decision Tree 생성시 필요한 Question list를 생성

bin/cluster-phones treeacc sets.int questions.int

(참고)input data 「sets.int」

1 2 3 4 5
6 7 8 9 10
11 12 13 14
15 16 17 18
19 20 21 22
23 24 25 26

phone index

  • 11〜14는 「I」、
  • 15〜18은 「M」、
  • 19〜22는 「O」、
  • 23〜26은 「S」,

컴파일 한다.

bin/compile-questions topo questions.int questions.qst

(참고)input data 「topo」

<Topology>
<TopologyEntry>
<ForPhones>
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
</ForPhones>
<State> 0 <PdfClass> 0 <Transition> 0 0.75 <Transition> 1 0.25 </State>
<State> 1 <PdfClass> 1 <Transition> 1 0.75 <Transition> 2 0.25 </State>
<State> 2 <PdfClass> 2 <Transition> 2 0.75 <Transition> 3 0.25 </State>
<State> 3 </State>
</TopologyEntry>
<TopologyEntry>
<ForPhones>
1 2 3 4 5 6 7 8 9 10
</ForPhones>
<State> 0 <PdfClass> 0 <Transition> 0 0.25 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 </State>
<State> 1 <PdfClass> 1 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 2 <PdfClass> 2 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 3 <PdfClass> 3 <Transition> 1 0.25 <Transition> 2 0.25 <Transition> 3 0.25 <Transition> 4 0.25 </State>
<State> 4 <PdfClass> 4 <Transition> 4 0.75 <Transition> 5 0.25 </State>
<State> 5 </State>
</TopologyEntry>
</Topology>

이어서, decision tree(결정트리)를 생성한다.

bin/build-tree treeacc roots.int questions.qst topo tree

(참고)input data 「roots.int」

shared split 1 2 3 4 5
shared split 6 7 8 9 10
shared split 11 12 13 14
shared split 15 16 17 18
shared split 19 20 21 22
shared split 23 24 25 26

tree(설명용으로 수정한 것)

ContextDependency
3   :context-width
1   :central-position

ToPdf
SE 1 [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ]{

   SE 1 [ 1 2 3 4 5 ]{
      CE 0
      SE 1 [ 6 7 8 9 10 ]{
         CE 1
         CE 2
      }
   } 

   SE 1 [ 15 16 17 18 ]{
      CE 3
      SE 1 [ 19 20 21 22 ]{
         CE 4
         CE 5
      } 
   }
} 
EndContextDependency 

tree를 그림으로 나타내면

녹색의 대괄호 안은 phone index를 나타낸다.

SE(SplitEventMap)의 key의 의미는 아래와 같다.

0(left phone)
1(center phone)
2(right phone)

갈색이 CE(ConstantEventMap)이고, leaf가 되는 pdf-id를 나타내고 있다.

생성된 결정트리를 가지고 Triphone의 초기 모델을 생성한다.

gmm-init-model tree treeacc topo tri_0.mdl

음성인식 메모(kaldi) 5 - gmm training 2

「原作者へ」

連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。 
正式に翻訳を許可されたいです。 
gogyzzz@gmail.comでご連絡ください。

다음 포스트의 번역입니다.

http://work-in-progress.hatenablog.com/entry/2018/03/25/111653


이전 편의 연속

학습용 그래프를 생성했다면, alignment를 생성하자.

초기 모델에 대해서는 「bin/align-equal-compiled」 커맨드를 사용한다.

bin/align-equal-compiled ark:fsts.0 ark:mosimosi.ark ark:equal.align.0

균등분할 alignment의 출력 결과는 아래와 같다.

input으로 전달하는 MFCC 파일의 198 프레임을 transition-id로 치환한다.

우변의 colon(:) 이후는 설명용으로 붙인 것이다.

균등분할 alignment (읽기 편하게 수정한 것)

utterance_id_001
  2   1   1   1   1   1  :sil(state0)                 
  8   5   5   5   5   5  :sil(state1)                    
 18  17  17  17  17  17  :sil(state4)              
206 205 205 205 205 205  :M_B(state0)        
208 207 207 207 207 207  :M_B(state1)        
210 209 209 209 209 209  :M_B(state2)        
242 241 241 241 241 241  :O_I(state0)        
244 243 243 243 243 243  :O_I(state1)        
246 245 245 245 245 245  :O_I(state2)        
266 265 265 265 265 265  :S_I(state0)        
268 267 267 267 267 267  :S_I(state1)        
270 269 269 269 269 269  :S_I(state2)        
194 193 193 193 193 193  :I_I(state0)        
196 195 195 195 195      :I_I(state1) 
198 197 197 197 197      :I_I(state2) 
218 217 217 217 217      :M_I(state0)
220 219 219 219 219      :M_I(state1)
222 221 221 221 221      :M_I(state2)
242 241 241 241 241      :O_I(state0)
244 243 243 243 243      :O_I(state1)
246 245 245 245 245      :O_I(state2)
266 265 265 265 265      :S_I(state0)
268 267 267 267 267      :S_I(state1)
270 269 269 269 269      :S_I(state2)
188 187 187 187 187      :I_E(state0)
190 189 189 189 189      :I_E(state1)
192 191 191 191 191      :I_E(state2)
  2   1   1   1   1      :sil(state0)
  6   5   5   5   5      :sil(state1)
 11  10  10  10  10      :sil(state2)
 13  15  15  15  15      :sil(state3)
  7   5   5   5   5      :sil(state1)
 14  15  15  15  15      :sil(state3)
 11  10  10  10  10      :sil(state2)
 14  15  15  15  15      :sil(state3)
 12  10  10  10  10      :sil(state2)
 18  17  17  17  17      :sil(state4)  

상태 전이(state transition)는 학습용 그래프를 기반으로 생성된다.

(gdb) p path
$25 = std::vector of length 49, capacity 64 = {0, 35, 1, 40, 33, 34, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 47, 28, 50, 29, 54, 30, 56, 28, 51, 30, 57, 29, 54, 30, 57, 29, 55, 31, 32}

숫자는 FST의 State-Id에 해당

    // First select path through ifst.
    vector<StateId> path;

결과는 랜덤이 될거라 생각했지만, 10번 반복해도 같다.

소스코드를 보니 kaldi::RandInt의 호출에서 3번째 변수를 전달하지 않는다.

fstext/fstext-utils-inl.h

size_t arc_offset = static_cast<size_t>(kaldi::RandInt(0, num_arcs_tot-1));

생략할 시 디폴트는 NULL이므로 Rand()의 입력은 같은 초기값이 사용되어 실행 결과가 같아진다.

base/kaldi-math.h

// Returns a random integer between first and last inclusive.
int32 RandInt(int32 first, int32 last, struct RandomState* state = NULL);

base/kaldi-math.cc

int32 RandInt(int32 min_val, int32 max_val, struct RandomState* state) {

균등 분할 alignment가 끝났다면 이것을 바탕으로 원래 통계량(statistics)을 구한다.

gmmbin/gmm-acc-stats-ali 0.mdl ark:mosimosi.ark ark:equal.align.0 0.acc

통계량의 출력 포맷은 아래와 같다.

맨 처음 괄호 안에는 transition-id 마다의 count, 그 후엔 pdf-id(phone、HMM-state로 식별 가능하도록 한 것)에 관한 것으로, 점유 수(OCCUPANCY) 인 것을 확인할 수 있다.

 [ 0 9 2 0 0 13 1 1 1 0 12 2 1 1 2 12 0 9 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 1 4 1 4 1 5 1 4 1 4 1 0 0 0 0 0 0 5 1 5 1 5 1 0 0 0 0 0 0 4 1 4 1 4 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 2 9 2 9 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 2 9 2 9 2 0 0 0 0 0 0 ]
<NUMPDFS> 98
<GMMACCS> <VECSIZE> 13 <NUMCOMPONENTS> 1 <FLAGS> 15 <OCCUPANCY>  [ 11 ]
<MEANACCS>  [
  583.025 -77.64178 -50.17585 77.67705 1.827717 -98.23819 141.6106 68.02332 -25.67511 138.0809 -28.37501 51.49424 -60.51704 ]
<DIAGVARACCS>  [
  31450.12 980.6454 1569.188 971.1459 888.6505 4001.74 2254.043 769.4111 1730.576 2365.113 1063.856 679.8729 789.1522 ]
</GMMACCS>
(도중 생략, 나머지 PDF-ID 97개의 내용이 이어진다)
<total_like> -678789.2 <total_frames> 198

이어서 학습을 진행한다.

gmmbin/gmm-est --min-gaussian-occupancy=3 0.mdl 0.acc 1.mdl

학습용 샘플이 적기 때문에 옵션을 추가하였다. 이렇게 하지 않으면 출현 빈도가 적은 pdf-id에 해당하는 모델이 업데이트 되지 않는다.

일부 모델이 초기 상태 그대로라면, 다음에 이어지는 alignment도 실패한다.

 --min-gaussian-occupancy    : MleDiagGmmOptions: Minimum occupancy to update a Gaussian.(float, default = 10)

이어서 alignment를 진행한다.

gmmbin/gmm-align-compiled 1.mdl ark:fsts.0 ark:mosimosi.ark ark:1.ali

이어서 통계량(statistics)를 생성하고, 모델을 학습한다. input만 바뀐 채로 나머지 과정은 이전과 같다.

gmmbin/gmm-acc-stats-ali 1.mdl ark:mosimosi.ark ark:1.ali 1.acc
gmmbin/gmm-est --min-gaussian-occupancy=3 1.mdl 1.acc 2.mdl

(참고)1.acc

 [ 0 8 2 0 0 8 1 0 1 0 24 1 1 0 1 12 0 5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 1 4 1 6 1 5 1 4 1 5 1 0 0 0 0 0 0 3 1 7 1 5 1 0 0 0 0 0 0 3 1 4 1 4 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 2 6 2 14 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 4 2 17 2 0 0 0 0 0 0 ]
<NUMPDFS> 98 

<GMMACCS> <VECSIZE> 13 <NUMCOMPONENTS> 1 <FLAGS> 15 <OCCUPANCY>  [ 10 ]
<MEANACCS>  [
  -115.1633 37.34524 52.23244 40.05222 16.03667 8.719225 85.01067 68.30832 11.52509 34.59558 62.21133 -23.4759 -52.60085 ]
<DIAGVARACCS>  [
  1822.415 538.1805 1578.469 610.5429 804.1371 3180.515 1147.177 790.0551 1260.287 790.6093 1341.816 235.106 707.7091 ]
</GMMACCS> 
(도중 생략, 나머지 PDF-ID 97개의 내용이 이어진다)
<total_like> -8444.121 <total_frames> 198 

가장 아랫 부분을 보면 score가 업데이트 되어 있는 것을 확인할 수 있다.