Tech Study/ASR/STT2020. 3. 5. 14:14

 

※ 이 포스트는 Cygwin을 통한 Unix-like 시스템 하에서의 빌드가 아닌, Visual Studio를 이용한 네이티브 패키지를 빌드하기 위한 포스트이다. 이하 내용을 보면 알겠지만, 이렇게 만들어진 패키지는 트레이닝이 불가능하여 standalone으로 사용할 수 없고, 빌드된 라이브러리를 자신의 프로젝트에 연동하여 기능을 활용하는 용도로만 사용이 가능하다. 그러므로, 원하는 목적에 부합하는지 먼저 확인 후 참조하기를 권해 드린다.

 

기계학습을 공부하려다 보니, 기계학습의 응용 분야 중 가장 유망한 분야 중 하나인 음성인식을 가지고 장난감(toy) 프로젝트를 만들어보고 싶어졌다.

 

당연히 내가 직접 만드는 건 불가능하고, 오픈소스를 이용해야 한다. 그래서...

https://www.goodfirms.co/blog/best-free-open-source-speech-recognition-software

위 사이트를 참조하여 서베이를 해보고 있는데, 그 중에 Kaldi라는 작품이 상당히 유명한 듯 하여 구동해보기로 했다.

Kaldi의 사이트에서 이런 저런 정보를 살펴본 걸 먼저 풀어보자면...

 

Site

https://github.com/kaldi-asr/kaldi

 

Logo

 

The name Kaldi

에디오피아 설화에서 커피 제조법를 발견한 염소 치기의 이름이라고 한다. 발음은 좀 알아보니, 그냥 '칼디' 라고 하면 되는 것 같다. 뜬금없긴 하지만, 위 로고를 보면 확실해지는데, 커피콩에다가 헤드폰을 씌워놨다. 아마도 개발진 중 한 분이 칼디 커피 브랜드를 좋아하지 않았을까 추측해본다. (역사적으로 유명한 많은 프로젝트들이 이런 장난스러운 이유로 작명이 되었다. C언어는 B 다음 알파벳, JAVA 언어는 자바 커피에서, Python은 TV쇼에서 따왔다는 것은 아주 유명한 예이다.)

 

Build

Linuxer로서의 직업은 4년 전에 중단되고, Windowser의 삶을 살고 있다. 나는 덕업일치이기 때문에 오픈소스도 윈도에서 빌드해야 겠다. 다행히 Kaldi는 아래와 같이 친절하게 가이드를 해주고 있다.

https://github.com/kaldi-asr/kaldi/blob/master/windows/INSTALL.md

이 후의 내용은 위 가이드를 따라간 기록이다.

 

Build - Notes

알려주고 싶은게 있는 모양이다. 몇몇 내용을 보면

  • 레시피 미지원. 윈도 버전에 재능기부하는 사람도 없고하니 전문개발자가 라이브러리 연동 용도로만 쓰길.
  • ATLAS(주:뭔지 모름)는 Cygwin 필수라 의미가 없어서 안했다.
  • CUDA를 지원하지 않음
  • 32비트 빌드를 하지 말길...
  • MSVC2017 이상만 지원됨
  • openfst-1.6.5 전용 (주:Finite State Transducer? IT의 길은 끝이 없구나...)
  • 윈도용 git은 필수

 

 

Build - Steps

본 게임에 들어가기 앞서 이슈가 하나 있는데, 내 PC에는 MSVC2019가 설치되어 있다. 가이드는 MSVC2017 기준이다. 하지만, 경험 상 크게 문제는 안 될 것 같다.

 

Compiling OpenFST

칼디를 빌드하기 위한 prerequisites 중 하나인 OpenFST를 먼저 빌드해야 한다.

Repo : https://github.com/kkm000/openfst.git

칼디 가이드에는 CMake를 이용해서 복잡하게 빌드환경을 구축하는 것으로 나오는데, 클론을 받아보면 이미 2년 전에 openfst.sln 이라는 VS2017 용 솔루션이 built-in 되어 있는 상태였다. 그걸 열면 되더라.

 

빌드 전에 세팅을 몇개 해줘야 했는데, 솔루션 내 모든 프로젝트에 대해 아래처럼 적당히 설정을 해줘야 한다.

본인에게 맞게 적당히 설정

 

설정을 마쳤으면 두근두근하는 마음으로 'F7' 을 눌러본다.

오오 잘되는 거 같다.

 

우하하, VS2019에서도 빌드가 매우 잘된다. 빌드 로그는 아래에 접어 두었으니 참고를.

더보기
1>------ Build started: Project: libfst, Configuration: Debug x64 ------
2>------ Build started: Project: libfstscript, Configuration: Debug x64 ------
1>compat.cc
2>arciterator-class.cc
2>arcsort.cc
1>flags.cc
1>fst-types.cc
1>fst.cc
1>mapped-file.cc
1>properties.cc
1>symbol-table-ops.cc
1>symbol-table.cc
1>util.cc
1>weight.cc
2>closure.cc
2>compile.cc
2>compose.cc
2>concat.cc
2>connect.cc
2>convert.cc
2>decode.cc
2>determinize.cc
2>difference.cc
2>disambiguate.cc
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file arciterator-class.cc)
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file difference.cc)
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file decode.cc)
2>draw.cc
2>encode.cc
2>encodemapper-class.cc
2>epsnormalize.cc
2>equal.cc
2>equivalent.cc
2>fst-class.cc
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file encode.cc)
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file encodemapper-class.cc)
2>getters.cc
2>info-impl.cc
2>info.cc
2>intersect.cc
2>invert.cc
2>isomorphic.cc
2>map.cc
2>minimize.cc
2>print.cc
2>project.cc
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file print.cc)
2>prune.cc
1>libfst.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\lib\libfst.lib
2>push.cc
2>randequivalent.cc
2>randgen.cc
2>relabel.cc
2>replace.cc
2>reverse.cc
2>reweight.cc
2>rmepsilon.cc
2>shortest-distance.cc
2>shortest-path.cc
2>stateiterator-class.cc
2>synchronize.cc
2>text-io.cc
2>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file stateiterator-class.cc)
2>topsort.cc
2>union.cc
2>verify.cc
2>weight-class.cc
2>libfstscript.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\lib\libfstscript.lib
2>Done building project "libfstscript.vcxproj".
3>------ Build started: Project: bin, Configuration: Debug x64 ------
3>fstclosure-main.cc
3>fstclosure.cc
3>fstepsnormalize-main.cc
3>fstequal-main.cc
3>fstequal.cc
3>fstepsnormalize.cc
3>fstinfo-main.cc
3>fstinfo.cc
3>fstcompile-main.cc
3>fstcompile.cc
3>fstreplace-main.cc
3>fstreplace.cc
3>fstprint-main.cc
3>fstprint.cc
3>fstarcsort-main.cc
3>fstarcsort.cc
3>fstequivalent-main.cc
3>fstequivalent.cc
3>fstinvert-main.cc
3>fstinvert.cc
3>fstrelabel-main.cc
3>fstrelabel.cc
3>fsttopsort-main.cc
3>fsttopsort.cc
3>fstpush-main.cc
3>fstpush.cc
3>fstreweight-main.cc
3>fstreweight.cc
3>fstsynchronize-main.cc
3>fstsynchronize.cc
3>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file fstprint-main.cc)
3>fstprune-main.cc
3>fstprune.cc
3>fstreverse-main.cc
3>fstreverse.cc
3>fstcompose-main.cc
3>fstrandgen-main.cc
3>fstminimize-main.cc
3>fstcompose.cc
3>fstrandgen.cc
3>fstminimize.cc
3>fstproject-main.cc
3>fstproject.cc
3>fstconvert-main.cc
3>fstconvert.cc
3>fstsymbols-main.cc
3>fstsymbols.cc
3>fstconnect-main.cc
3>fstconnect.cc
3>fstunion-main.cc
3>fstunion.cc
3>fstencode-main.cc
3>fstencode.cc
3>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file fstencode-main.cc)
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstinfo.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstrelabel.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstequal.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstarcsort.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fsttopsort.exe
3>fstshortestpath-main.cc
3>fstshortestpath.cc
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstsynchronize.exe
3>fstshortestdistance-main.cc
3>fstshortestdistance.cc
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstpush.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstcompile.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstclosure.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstreweight.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstinvert.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstequivalent.exe
3>Done building project "bin.vcxproj".
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstconnect.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstsymbols.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstproject.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstshortestpath.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstprint.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstprune.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstshortestdistance.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstencode.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstepsnormalize.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstminimize.exe
3>fstdisambiguate-main.cc
3>fstdeterminize-main.cc
3>fstdisambiguate.cc
3>fstdifference-main.cc
3>fstconcat-main.cc
3>fstintersect-main.cc
3>fstrmepsilon-main.cc
3>fstconcat.cc
3>fstdifference.cc
3>fstdeterminize.cc
3>fstisomorphic-main.cc
3>fstisomorphic.cc
3>fstintersect.cc
3>fstrmepsilon.cc
3>E:\dev\git\openfst\src\include\fst\complement.h(1,1): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss (compiling source file fstdifference-main.cc)
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstunion.exe
3>Done building project "bin.vcxproj".
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstconvert.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstreplace.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstconcat.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstcompose.exe
3>fstdraw-main.cc
3>fstdraw.cc
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstdifference.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstintersect.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstdeterminize.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstrandgen.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstreverse.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstisomorphic.exe
3>fstmap-main.cc
3>fstmap.cc
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstdisambiguate.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstdraw.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstrmepsilon.exe
3>bin.vcxproj -> E:\dev\git\openfst\build_output\x64\Debug\bin\fstmap.exe
========== Build: 3 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

 

Compiling Kaldi

본체를 빌드하는 섹션인데, 선행 조건에 대한 내용도 있고, 리눅스 스러운 설명에 미리 설치되어 있어야 하는 툴에 대한 설명도 누락되어 있고, 옵션도 다양하고... 아주 복작복작하다. 때문에, 나이브하게 딱 내가 원하는 목표만 이룰 수 있게 나만의 시퀀스를 써내려가 보련다.

 

1. Installing Perl

칼디의 솔루션 파일을 만들어주는 스크립트가 perl로 되어 있기 때문에 필수.

http://strawberryperl.com/

 

2. Cloning Kaldi

https://github.com/kaldi-asr/kaldi

 

3. Installing OpenBLAS

선형대수학을 위한 라이브러리가 필요하다는데, Intel® MKLOpenBLAS 둘 중에 택일을 해야 한다. OpenBLAS로 선택하였다. OpenBLAS 홈페이지에 기재된 최신 버전은 0.3.9지만, 칼디 가이드에 나온대로 0.2.14 버전을 연동하도록 한다.

아래 파일들을 다운로드 받은 후,

http://sourceforge.net/projects/openblas/files/v0.2.14/OpenBLAS-v0.2.14-Win64-int32.zip

http://sourceforge.net/projects/openblas/files/v0.2.14/mingw64_dll.zip

kaldi/tools 에 압축을 풀어 둔다.

 

4. Setting Build Properties

우선 아래와 같이 설정 파일을 만들어 둔다.

> cd kaldi\windows
> copy variables.props.dev variables.props
> copy kaldiwin_openblas.props kaldiwin.props

 

variables.props를 수정해야 한다. 8~12줄을 참고.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  <PropertyGroup Label="UserMacros">
    <!-- Change the following paths so they are correct on your machine -->
    <!-- Do not modify anything before this line -->
    <MKLDIR>C:\Program Files (x86)\IntelSWTools\compilers_and_libraries\windows\mkl\</MKLDIR>
    <!-- 아래 세 개의 태그(OPENBLASDIR, OPENFST, OPENFSTLIB)만 수정하면 됨 -->
    <!-- 절대경로를 입력할 것을 추천함 -->
    <OPENBLASDIR>E:\dev\git\kaldi\tools\OpenBLAS-v0.2.14-Win64-int32</OPENBLASDIR>    
    <OPENFST>E:\dev\git\kaldi\openfst</OPENFST>
    <OPENFSTLIB>E:\dev\git\openfst</OPENFSTLIB>
    <CUBDIR>c:\Users\jtrmal\Documents\cub\</CUBDIR>
    <NVTOOLSDIR>C:\Program FIles\NVIDIA Corporation\NvToolsExt\</NVTOOLSDIR>
    <!-- Do not modify anything after this line -->
  </PropertyGroup>
  <PropertyGroup />
  <ItemDefinitionGroup />
  ...
  ...

 

또, kaldi/windows 안에 있는 openfstwin*.props 를 수정해야 한다. OpenFST 정보가 조금 안맞는 것 같다.

openfstwin_debug.props 를 예로 들겠다. 11~13줄을 확인하시길.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <ClCompile>
      <AdditionalIncludeDirectories>$(OPENFST)\src\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <!-- 아래 두 줄을 수정해야 실제 경로와 파일명에 맞게 수정해줘야 한다. -->
      <AdditionalLibraryDirectories>$(OPENFSTLIB)/build_output/x64/Debug/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
      <AdditionalDependencies>libfst.lib;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalOptions> %(AdditionalOptions)</AdditionalOptions>
    </Link>
  </ItemDefinitionGroup>
</Project>

 

5. Generating MSVC Solution

> cd /D (kaldi)\windows
> perl generate_solution.pl --vsver vs2017 --enable-openblas

 

생성 도중에 아래의 에러를 만날 수 있다.

INFO: parsing E:/dev/git/kaldi/windows/../src/cudadecoder/Makefile
INFO: parsing E:/dev/git/kaldi/windows/../src/cudadecoderbin/Makefile
INFO: parsing E:/dev/git/kaldi/windows/../src/online/Makefile
INFO: parsing E:/dev/git/kaldi/windows/../src/onlinebin/Makefile
ERROR?: file E:\dev\git\kaldi\windows\..\src\cudadecoder\cuda-decoder-kernels.cc not found - project kaldi-cudadecoder

 

에러를 없애기 위해 안내(https://github.com/kaldi-asr/kaldi/issues/3584)를 따라 (kaldi)/src/Makefile 을 수정해야 한다. 아래 12, 13번 줄 참고.

# This is the top-level Makefile for Kaldi.
# Also see kaldi.mk which supplies options and some rules
# used by the Makefiles in the subdirectories.

SHELL := /bin/bash

SUBDIRS = base matrix util feat cudafeat tree gmm transform \
          fstext hmm lm decoder lat kws cudamatrix nnet \
          bin fstbin gmmbin fgmmbin featbin cudafeatbin \
          nnetbin latbin sgmm2 sgmm2bin nnet2 nnet3 rnnlm chain nnet3bin nnet2bin kwsbin \
          ivector ivectorbin online2 online2bin lmbin chainbin rnnlmbin \
          cudadecoderbin
#          cudadecoder cudadecoderbin

MEMTESTDIRS = base matrix util feat cudafeat tree gmm transform \
          fstext hmm lm decoder lat nnet kws chain \

 

수정하고 나서 다시 sln 생성 스크립트를 돌리면 정상적으로 sln 파일이 생성된다.

위치 : (kaldi)\kaldiwin_vs2017_OPENBLAS\kaldiwin_vs2017.sln

 

6. Getting 'version'

> cd /D (kaldi)\windows
> perl get_version.pl

 

7. Compiling

이렇게 생성한 솔루션을 열어보면 648개의 프로젝트를 로딩하느라 상당한 시간이 걸린다. 기본 VS2017이므로 VS2019로 리타겟팅해주고(이 것도 하세월), 더욱 두근거리는 마음으로 'F7' 을 눌러 본다.

시작은 되는 것 같은데...

규모가 규모인지라 상당한 시간이 필요하니, 애연가는 담배 한대, 커피애호가는 커피 한잔의 여유를...

완료!

22개의 프로젝트가 빌드에 실패했는데, 10개는 가이드에서 언급된 프로젝트이고, 12개는 CUDA 관련 프로젝트로 보인다. 즉, 빌드 자체에는 성공했다는 의미이다.

 

By the Way

이 작업이 아무 쓸모없는 작업 임을 깨닫기까지 그리 오랜 시간이 걸리지 않았다. 제작자가 설치 가이드 시작 부분에 왜 경고성 안내를 기술했는지 뒤늦게 이해하게 된 것이다.

빌드를 성공시키고, 사용을 해보기 위해 레퍼런스 가이드를 살펴 보았으나, 모두 Unix-like 시스템에서만 따라할 수 있는 내용이었고, 무엇보다 egs에 미리 구성된 레시피를 이용해야 하는데 윈도 네이티브로는 원천적으로 사용이 불가능하니 아무 것도 할 수가 없었다.

레시피를 윈도용으로 직접 포팅하여 구축을 해볼 수도 있겠지만, 성공여부가 불확실한 상태에서 그런 노력을 기울일 시간에 다른 대안을 찾아보는 게 더 나은 선택이 되겠다.

Cygwin을 이용하는 것이 좋은 접근법이기는 하지만, 내가 원하는 방향은 아니기 때문에 다음에 기회가 될 때 시도해보는 것으로 하고, 다른 대안을 찾아보도록 해야 겠다.

 

 

Posted by JMAN