파이썬3 노트

allennlp의 elmo.md 알고 싶은 부분만 적당히 직역

Jonchann 2019. 5. 12. 18:41

출처: https://github.com/allenai/allennlp/blob/master/tutorials/how_to/elmo.md

ELMo: Deep contextualized word representations (ELMo: 깊은 문맥정보를 포함하는 단어 분산 표현)

대규모 쌍방향 언어모델 등으로 미리 훈련된 문맥정보를 포함하는 단어들의 분산표현은 질의응답, 동일 지시어, 의미론적 역할 라벨링, 분류, 구문분석 등의 많은 자연어처리의 지도학습 연구에서 GloVe나 word2vec보다 뛰어난 성과를 보여주었다.

 

이 문서는 pytorch와 allennlp를 이용해 당신의 모델에 ELMo 분산표현을 삽입하는 방법을 설명하기 위한 것이다. 물론 tensorflow로 구현하는 방법도 적어놓았다.

 

ELMo에 관한 더 자세한 내용을 알고 싶다면 "Deep contextualized word representations", NAACL 2018 혹은 AllenNLP 홈페이지의 ELMo 섹션을 참고하길 바란다.

 

인용:

@inproceedings{Peters:2018,
  author={Peters, Matthew E. and  Neumann, Mark and Iyyer, Mohit and Gardner, Matt and Clark, Christopher and Lee, Kenton and Zettlemoyer, Luke},
  title={Deep contextualized word representations},
  booktitle={Proc. of NAACL},
  year={2018}
}
@inproceedings{Gardner2017AllenNLP,
  title={{AllenNLP}: A Deep Semantic Natural Language Processing Platform},
  author={Matt Gardner and Joel Grus and Mark Neumann and Oyvind Tafjord
    and Pradeep Dasigi and Nelson F. Liu and Matthew Peters and
    Michael Schmitz and Luke S. Zettlemoyer},
  year={2018},
  booktitle={ACL workshop for NLP Open Source Software}
}

 

Writing contextual representations to disk (디스크에 문맥정보를 포함하는 분산표현을 작성하기)

당신은 elmo커맨드를 통해 디스크에 ELMo 분산표현을 작성할 수 있다. elmo커맨드는 문장 데이터 셋을 위한 모든 쌍방향 언어모델의 각 레이어를 HDF5 확장 파일로 작성할 것이다. 이렇게 생성된 hdf5 파일에는 원문의 줄 인덱스를 key 정보로 포함되어 있다. 아래가 elmo커맨드의 사용례이다.

echo "The cryptocurrency space is now figuring out to have the highest search on Google globally ." > sentences.txt
echo "Bitcoin alone has a sixty percent share of global search ." >> sentences.txt
allennlp elmo sentences.txt elmo_layers.hdf5 --all

만약 당신이 원문 데이터셋을 포함하지 않는 ELMo 분산표현을 사용하고 싶다면 --include-sentence-indices를 사용하는 것으로 JSON 형식의 문자열로 작성하면 된다. 이 때에 문장들의 줄 인덱스 정보는 "sentence_indices" key로 맵핑된다. 더 자세한 사항은 allennlp elmo -h에서 확인하길 바란다.

 

당신이 한 번 ELMo 벡터들을 HDF5 파일로 작성하면 당신은 여러 HDF5 라이브러리를 통해 파일을 읽어올 수 있다. 예를 들면, h5py 등을 통해 아래와 같이 읽어올 수 있다:

import h5py
h5py_file = h5py.File("elmo_layers.hdf5", 'r')
embedding = h5py_file.get("0")
assert(len(embedding) == 3) # one layer for each vector
assert(len(embedding[0]) == 16) # one entry for each word in the source sentence

 

Using ELMo as a PyTorch Module to train a new model (ELMo를 새로운 모델을 훈련하기 위한 PyTorch 모듈로 사용하기)

ELMo를 통해 모델을 훈련시키기 위해서는 allennlp.modules.elmo.Elmo 클래스 (API doc)를 사용하면 된다. 이 클래스는 PyTorch 텐서 형태로 가중 ELMo 분산표현을 처리하기 위한 방법을 제공한다. 가중 평균은 대규모 모델의 한 파트로 학습될 수 있으며 특정 태스크에서 성능을 향상시키기 위한 용도로 사용하면 가장 좋은 성능을 얻을 수 있다.

 

이것은 torch.nn.Module의 부분 클래스이며 ELMo 분산표현의 그 어떤 숫자도 처리한다. 그리고 각각에게 훈련 가능한 스칼라 가중치를 넘긴다. 예를 들어, 아래의 코드는 2층 표현을 처리한다 (우리 논문에 나오는 SNLI와 SQuAD 모델 처럼) :

from allennlp.modules.elmo import Elmo, batch_to_ids

options_file = "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_options.json"
weight_file = "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_weights.hdf5"

# Compute two different representation for each token.
# Each representation is a linear weighted combination for the
# 3 layers in ELMo (i.e., charcnn, the outputs of the two BiLSTM))
elmo = Elmo(options_file, weight_file, 2, dropout=0)

# use batch_to_ids to convert sentences to character ids
sentences = [['First', 'sentence', '.'], ['Another', '.']]
character_ids = batch_to_ids(sentences)

embeddings = elmo(character_ids)

# embeddings['elmo_representations'] is length two list of tensors.
# Each element contains one layer of ELMo representations with shape
# (2, 3, 1024).
#   2    - the batch size
#   3    - the sequence length of the batch
#   1024 - the length of each ELMo vector

만약 당신이 pytorch 모델을 훈련하고있지 않다면, 단지 numpy 배열을 출력하고 싶은거라면 allennlp.commands.elmo.ElmoEmbedder를 사용해라.

 

Using ELMo interactively (대화식 ELMo 사용하기)

당신은 iPython을 이용해 대화식으로 (혹은 계획적으로) ELMo를 사용할 수 있다. allennlp.commands.elmo.ElmoEmbedder클래스는 하나의 혹은 복수의 문장들을 ELMo로 처리하는 가장 쉬운 방법을 제공한다. 하지만 이는 numpy 배열이 반환되기 때문에 독립된 커맨드로 대규모가 아닌 모델에서 사용해야 할 것이다. 예를 들어, 만약 당신이 ELMo 벡터에서 가중 평균을 학습하고 싶을 때 당신은 allennlp.modules.elmo.Elmo를 대신 사용해야 한다.

 

ElmoEmbedder 클래스는 각 단어에 대해 3 개 벡터를 반환한다. 각 벡터는 ELMo의 LSTM 출력값 속 각 레이어에 해당한다. 가장 첫 번째 레이어는 문맥을 이해하지 못하는 토큰 표현에 해당하고 두 번째 레이어가 이를 잇는다. 각 레이어가 어떤 정보를 캐치하는지 알고 싶다면 ELMo 논문 혹은 EMNLP 2018에서 선보인 결과를 찾아보기 바란다.

$ ipython
> from allennlp.commands.elmo import ElmoEmbedder
> elmo = ElmoEmbedder()
> tokens = ["I", "ate", "an", "apple", "for", "breakfast"]
> vectors = elmo.embed_sentence(tokens)

> assert(len(vectors) == 3) # one for each layer in the ELMo output
> assert(len(vectors[0]) == len(tokens)) # the vector elements correspond with the input tokens

> import scipy
> vectors2 = elmo.embed_sentence(["I", "ate", "a", "carrot", "for", "breakfast"])
> scipy.spatial.distance.cosine(vectors[2][3], vectors2[2][3]) # cosine distance between "apple" and "carrot" in the last layer
0.18020617961883545

 

Using ELMo with existing allennlp models (ELMo를 현존하는 allennlp 모델과 사용하기)

ELMo를 현존하는 모델에 추가하는 것이 가장 쉬운 설정 변경 방법이다. 우리가 제공하는 TokenEmbedder는 문자 번호를 입력값으로 받아 쌍방향 언어모델을 실행시켜 학습된 가중치 결합을 통해 ELMo 분산표현을 계산한다. 이 간단한 예시는 마지막 모델의 ELMo 분산표현의 하나의 레이어만을 포함한다는 것에 주의해라. SQuAD와 SNLI와 같은 다른 상황에서 우리는 여러 층을 쌓는 것이 성능을 높인다는 사실을 발견했다. 여러 층을 쌓으려면 코드를 수정해야 한다 (아래를 참조) .

 

우리는 SRL 모델 설정 파일을 코드 수정 예시로 들 것이다. 여기엔 ELMo가 없으며 100 차원의 미리 훈련된 GloVe 벡터를 사용했다.

ELMo를 추가하기 위해 수정해야 할 곳은 3 군데 이다. 먼저 text_field_embedder섹션을 elmo섹션에 추가한다:

"text_field_embedder": {
  "tokens": {
    "type": "embedding",
    "embedding_dim": 100,
    "pretrained_file": "https://s3-us-west-2.amazonaws.com/allennlp/datasets/glove/glove.6B.100d.txt.gz",
    "trainable": true
  },
  "elmo": {
    "type": "elmo_token_embedder",
    "options_file": "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_options.json",
    "weight_file": "https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_weights.hdf5",
    "do_layer_norm": false,
    "dropout": 0.5
  }
}

두 번째로, GloVe의 id 외에도 날 것 그대로의 텍스트를 ELMo 문자 id로 변환하기 위해 dataset_readerelmo를 추가해야 한다:

"dataset_reader": {
  "type": "srl",
  "token_indexers": {
    "tokens": {
      "type": "single_id",
      "lowercase_tokens": true
    },
    "elmo": {
      "type": "elmo_characters"
    }
  }
}

세 번째로, 여러 층의 LSTM 부호기의 입력 차원값 (input_size) 을 수정해야 한다. 베이스라인 모델은 입력값의 차원이 200이었다 (100 차원의 CloVe 분산표현과 예측 위치를 특정짓는 100 차원의 특징값) . ELMo는 1024 차원의 분산표현을 반환하기 때문에 새로운 input_size는 1224가 된다.

"encoder": {
  "type": "alternating_lstm",
  "input_size": 1224,
  "hidden_size": 300,
  "num_layers": 8,
  "recurrent_dropout_probability": 0.1,
  "use_highway": true
}

 

Recommended hyper-parameter settings for Elmo class (Elmo 클래스를 위해 추장하는 하이퍼 파라메터 설정)

ELMo를 사용할 때 설정해야 하는 몇 가지 하이퍼 파라메터가 있다. 일반적인 규칙처럼 훈련에서는 하이퍼 파라메터에 비교적 둔감해지지만, 그럼에도 불구하고 처음 훈련을 실행할 때를 위한 몇 가지 일반적인 가이드라인이 있다.

 

  • 같은 위치에서 단층 ELMo 분산표현을 미리 훈련된 단어 분산표현으로 계산에 넣어라
  • Elmo 클래스를 구성할 때 do_layer_norm=False로 설정해라
  • Elmo클래스에 직접적으로 혹은 당신의 네트워크의 다음 레이어에 드롭아웃 (0.5가 좋은 기본 설정값이다)을 추가해라
  • 파라메터 (논문에서는 lambda=0.001 사용)를 가중시키면서 스칼라에 작은 양의 L2 정규화를 추가해라. 이것들은 쌍방향 언어모델 레이어의 인덱스인 X=[0, 1, 2]scalar_mix_L.scalar_parameters.X라는 파라메터이다. 그리고 L은 모델 속에 포함되어 있는 ELMo 분산표현의 인덱스를 만든다. 종종 이러한 파라메터들의 정규화 없이도 대규모 데이터셋을 이용한 모델의 성능이 약간 올라가기도 하지만, 가끔 일어나는 일이기 때문에 되려 훈련이 안정적이지 못하게 된다.

마지막으로, 우리는 ELMo만 사용할 때 보다 다른 GloVe 등의 미리 훈련된 단어 벡터들을 함께 사용하는 것이 성능을 향상시킬 때도 있다는 것을 발견했지만 훈련 속도는 느려진다. 하지만 가장 좋은 결과를 위해 모든 가능성을 실험해보는 것을 추천한다.