deeplearning4j의 Word2Vec에 Kuromoji를 도입했습니다.

소개



웹 API 마켓플레이스Apitore에 Word2Vec을 추가하려고 합니다. Word2Vec이 있으면 자연 언어 처리 시스템의 응용 프로그램에서 다양한 확산이 나옵니다. 그 이야기는 API를 공개했을 때로 이번에는 Java에서 Word2Vec을 구현하는 노하우를 공개합니다. Java로 Word2Vec을 만드는 경우, 본가의 Google에서도 추천하고 있는 deeplearning4j 를 사용하면 간단합니다. 내장하고 있는 형태소 해석 기능은 스페이스 단락이므로, 일본어 형태소 해석기의 Kuromoji를 사용합니다.
소스 코드 등은 여기 으로 공개하고 있습니다.

amarec (20160919-095417)

deeplearning4j word2vec



Java를 아무래도 사용하고 싶기 때문에, deeplearning4j 를 이용합니다. 형태소 분석기는 Kuromoji입니다. 이번에는 Maven을 사용합니다.
<dependency>
  <groupId>com.atilika.kuromoji</groupId>
  <artifactId>kuromoji-ipadic</artifactId>
  <version>0.9.0</version>
</dependency>

<dependency>
  <groupId>org.deeplearning4j</groupId>
  <artifactId>deeplearning4j-ui</artifactId>
  <version>0.5.0</version>
</dependency>

<dependency>
  <groupId>org.deeplearning4j</groupId>
  <artifactId>deeplearning4j-nlp</artifactId>
  <version>0.5.0</version>
</dependency>

<dependency>
  <groupId>org.nd4j</groupId>
  <artifactId>nd4j-native</artifactId>
  <version>0.5.0</version>
</dependency>

deeplearning4j의 word2vec에서 kuromoji를 활용하기 위해 확장 클래스를 구현합니다. Tokenizer와 TokenizerFactory의 확장 클래스입니다. 이미 Scala에서 비슷한 일을 한 사람이 있었으므로 그쪽을 참고로했습니다.
public class KuromojiIpadicTokenizer implements Tokenizer {

  private List<Token> tokens;
  private int index;
  private TokenPreProcess preProcess;

  public KuromojiIpadicTokenizer (String toTokenize) {
    com.atilika.kuromoji.ipadic.Tokenizer tokenizer = new com.atilika.kuromoji.ipadic.Tokenizer();
    tokens = tokenizer.tokenize(toTokenize);
    index = (tokens.isEmpty()) ? -1:0;
  }


  @Override
  public int countTokens() {
    return tokens.size();
  }

  @Override
  public List<String> getTokens() {
    List<String> ret = new ArrayList<String>();
    while (hasMoreTokens()) {
      ret.add(nextToken());
    }
    return ret;
  }

  @Override
  public boolean hasMoreTokens() {
    if (index < 0)
      return false;
    else
      return index < tokens.size();
  }

  @Override
  public String nextToken() {
    if (index < 0)
      return null;

    Token tok = tokens.get(index);
    index++;
    if (preProcess != null)
      return preProcess.preProcess(tok.getSurface());
    else
      return tok.getSurface();
  }

  @Override
  public void setTokenPreProcessor(TokenPreProcess preProcess) {
    this.preProcess = preProcess;
  }

}
public class KuromojiIpadicTokenizerFactory implements TokenizerFactory {

  private TokenPreProcess preProcess;


  @Override
  public Tokenizer create(String toTokenize) {
    if (toTokenize == null || toTokenize.isEmpty()) {
      throw new IllegalArgumentException("Unable to proceed; no sentence to tokenize");
    }

    KuromojiIpadicTokenizer ret = new KuromojiIpadicTokenizer(toTokenize);
    ret.setTokenPreProcessor(preProcess);
    return ret;
  }

  @Override
  public Tokenizer create(InputStream paramInputStream) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void setTokenPreProcessor(TokenPreProcess preProcess) {
    this.preProcess = preProcess;
  }

  @Override
  public TokenPreProcess getTokenPreProcessor() {
    return this.preProcess;
  }

}

글쎄, 실제로 배워 보겠습니다. 학습 데이터의 로드는 다음과 같이 합니다.
SentenceIterator iter = new BasicLineIterator(new File("corpus.txt"));

형태소 해석의 실행은 다음과 같이 합니다. 형태소 해석 실행 후에 영 단어의 활용형 부분(e.g. -ed,-ing)의 제거, 영문자 소문자화, 숫자의 기호화를 해 둡니다.
final EndingPreProcessor preProcessor    = new EndingPreProcessor();
KuromojiIpadicTokenizerFactory tokenizer = new KuromojiIpadicTokenizerFactory();
tokenizer.setTokenPreProcessor( new TokenPreProcess()
{
  @Override
  public String preProcess( String token )
  {
    token       = token.toLowerCase();
    String base = preProcessor.preProcess( token );
    base        = base.replaceAll( "\\d" , "__NUMBER__" );
    return base;
  }
});

학습을 수행합니다. 파라미터는 넷에서 조사해 자주(잘) 사용되고 있을 것 같은 것을 채용하고 있으므로, 적당합니다. 코어는 6개 사용하도록 했습니다만, 태스크 매니저를 보면 6개는 사용하지 않는 것 같습니다.
int batchSize   = 1000;
int iterations  = 5;
int layerSize   = 150;

Word2Vec vec = new Word2Vec.Builder()
    .batchSize(batchSize)
    .minWordFrequency(5)
    .useAdaGrad(false)
    .layerSize(layerSize)
    .iterations(iterations)
    .seed(1)
    .windowSize(5)
    .learningRate(0.025)
    .minLearningRate(1e-3)
    .negativeSample(10)
    .iterate(iter)
    .tokenizerFactory(tokenizer)
    .workers(6)
    .build();
vec.fit();

학습 모델을 저장합니다. 이것을 잊으면 지옥입니다.
WordVectorSerializer.writeWordVectors(vec, "model-wordvectors.txt");

학습이 완료된 후에는 만든 모델을 사용하여 알레콜레할 수 있습니다.
WordVectors vec = WordVectorSerializer.loadTxtVectors(new File("model-wordvectors.txt"));
Collection<String> lst = vec.wordsNearest("day", 10);
System.out.println(lst);
double cosSim = vec.similarity("day", "night");
System.out.println(cosSim);
double[] wordVector = wordVectors.getWordVector("day");
System.out.println(wordVector);

결론



deeplearning4j를 사용하여 Word2Vec을 쉽게 구현할 수있었습니다. 중요한 데모에 대해서는 준비 중입니다. API를 게시할 때 데모 결과를 포함하여 기사를 작성합니다. 덧붙여서 작은 데이터로 동작 확인은 하고 있으므로, 상기의 기사는 정확합니다. 현재 Windows10 64bit corei7, 메모리 10GB를 사용하여 학습 중입니다. 원 이틀이 지나도 끝나지 않습니다. 메모리는 10GB 지정했습니다만, 대체로 3GB~5GB정도 밖에 사용하지 않는 것 같습니다.

좋은 웹페이지 즐겨찾기