「原作者へ」
連絡先を存じ上げませんでしたので、不本意ながら無断で翻訳しました。
正式に翻訳を許可されたいです。
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)가 된다.
댓글 없음:
댓글 쓰기