模型:

monsoon-nlp/bert-base-thai

英文

BERT-th

从HuggingFace/Transformers库的 https://github.com/ThAIKeras/bert 适配而来

预分词

您必须运行原始的ThaiTokenizer,以使您的分词与原始模型相匹配。

如果您跳过此步骤,您的效果将不会比mBERT或随机猜测好多少!

Refer to this CoLab notebook 或按照以下步骤:

pip install pythainlp six sentencepiece python-crfsuite
git clone https://github.com/ThAIKeras/bert
# download .vocab and .model files from ThAIKeras/bert > Tokenization section

或来自 .vocab .model 的链接。

然后设置ThaiTokenizer类——这略微修改了TensorFlow的依赖。

import collections
import unicodedata
import six

def convert_to_unicode(text):
  """Converts `text` to Unicode (if it's not already), assuming utf-8 input."""
  if six.PY3:
    if isinstance(text, str):
      return text
    elif isinstance(text, bytes):
      return text.decode("utf-8", "ignore")
    else:
      raise ValueError("Unsupported string type: %s" % (type(text)))
  elif six.PY2:
    if isinstance(text, str):
      return text.decode("utf-8", "ignore")
    elif isinstance(text, unicode):
      return text
    else:
      raise ValueError("Unsupported string type: %s" % (type(text)))
  else:
    raise ValueError("Not running on Python2 or Python 3?")

def load_vocab(vocab_file):
  vocab = collections.OrderedDict()
  index = 0
  with open(vocab_file, "r") as reader:
    while True:
      token = reader.readline()
      if token.split(): token = token.split()[0] # to support SentencePiece vocab file
      token = convert_to_unicode(token)
      if not token:
        break
      token = token.strip()
      vocab[token] = index
      index += 1
  return vocab

#####

from bert.bpe_helper import BPE
import sentencepiece as spm

def convert_by_vocab(vocab, items):
  output = []
  for item in items:
    output.append(vocab[item])
  return output

class ThaiTokenizer(object):
  """Tokenizes Thai texts."""

  def __init__(self, vocab_file, spm_file):
    self.vocab = load_vocab(vocab_file)
    self.inv_vocab = {v: k for k, v in self.vocab.items()}

    self.bpe = BPE(vocab_file)    
    self.s = spm.SentencePieceProcessor()
    self.s.Load(spm_file)

  def tokenize(self, text):
    bpe_tokens = self.bpe.encode(text).split(' ')
    spm_tokens = self.s.EncodeAsPieces(text)

    tokens = bpe_tokens if len(bpe_tokens) < len(spm_tokens) else spm_tokens

    split_tokens = []

    for token in tokens:
      new_token = token

      if token.startswith('_') and not token in self.vocab:
        split_tokens.append('_')
        new_token = token[1:]

      if not new_token in self.vocab:
        split_tokens.append('<unk>')
      else:
        split_tokens.append(new_token)

    return split_tokens

  def convert_tokens_to_ids(self, tokens):
    return convert_by_vocab(self.vocab, tokens)

  def convert_ids_to_tokens(self, ids):
    return convert_by_vocab(self.inv_vocab, ids)

然后对自己的文本进行预分词:

from pythainlp import sent_tokenize
tokenizer = ThaiTokenizer(vocab_file='th.wiki.bpe.op25000.vocab', spm_file='th.wiki.bpe.op25000.model')  

txt = "กรุงเทพมหานครเป็นเขตปกครองพิเศษของประเทศไทย มิได้มีสถานะเป็นจังหวัด คำว่า \"กรุงเทพมหานคร\" นั้นยังใช้เรียกองค์กรปกครองส่วนท้องถิ่นของกรุงเทพมหานครอีกด้วย"
split_sentences = sent_tokenize(txt)
print(split_sentences)
"""
['กรุงเทพมหานครเป็นเขตปกครองพิเศษของประเทศไทย ',
 'มิได้มีสถานะเป็นจังหวัด ',
 'คำว่า "กรุงเทพมหานคร" นั้นยังใช้เรียกองค์กรปกครองส่วนท้องถิ่นของกรุงเทพมหานครอีกด้วย']
"""

split_words = ' '.join(tokenizer.tokenize(' '.join(split_sentences)))
print(split_words)
"""
'▁กรุงเทพมหานคร เป็นเขต ปกครอง พิเศษ ของประเทศไทย ▁มิ ได้มี สถานะเป็น จังหวัด ▁คําว่า ▁" กรุงเทพมหานคร " ▁นั้น...' # continues
"""

原始README如下:

谷歌的 BERT 目前是文本表示预训练方法中的最先进方法,同时还提供多语言模型。不幸的是,泰语是103种语言中唯一被排除在外的,原因是泰语的词分割很困难。

BERT-th基于BERT-Base结构提供了仅限泰语的预训练模型。现在可以下载。

BERT-th还包括相关的代码和脚本,以及预训练模型,这些都是在原始BERT项目中修改的版本。

预处理

数据来源

BERT-th的训练数据来自 the latest article dump of Thai Wikipedia ,日期为2018年11月2日。原始文本是通过 WikiExtractor 提取的。

句子分割

在经过BERT模块进一步处理之前,输入数据需要分割为单独的句子。由于泰语中没有明确的句子结束标记,要确定句子边界是相当困难的。据我们所知,还没有其他地方实现泰语句子分割。因此,在这个项目中,我们采用简单的启发式方法进行句子分割,考虑空格、句子长度和常见的连接词。

预处理之后,训练语料库包括约200万个句子和4000万个单词(计算 PyThaiNLP 进行词分割后的单词)。可以下载未格式化和分割的文本 here

分词

BERT使用 WordPiece 作为分词机制。但它是Google内部使用的,我们不能应用现有的泰语词分割,然后利用WordPiece来学习一组子词单元。最好的替代方案是实施 SentencePiece ,它实现了 BPE 并且不需要词分割。

在这个项目中,我们采用了 BPEmb 的预训练泰语SentencePiece模型。选择了25000个词汇的模型,词汇文件必须添加BERT的特殊字符,包括'[PAD]', '[CLS]', '[SEP]'和'[MASK]'。可以下载模型和词汇文件 here

SentencePiece和bpe_helper.py都用于对数据进行分词。在BERT的tokenization.py中添加了ThaiTokenizer类用于对泰语文本进行分词。

预训练

可以使用此脚本在预训练之前准备数据。

export BPE_DIR=/path/to/bpe
export TEXT_DIR=/path/to/text
export DATA_DIR=/path/to/data

python create_pretraining_data.py \
  --input_file=$TEXT_DIR/thaiwikitext_sentseg \
  --output_file=$DATA_DIR/tf_examples.tfrecord \
  --vocab_file=$BPE_DIR/th.wiki.bpe.op25000.vocab \
  --max_seq_length=128 \
  --max_predictions_per_seq=20 \
  --masked_lm_prob=0.15 \
  --random_seed=12345 \
  --dupe_factor=5 \
  --thai_text=True \
  --spm_file=$BPE_DIR/th.wiki.bpe.op25000.model

然后,可以使用以下脚本从头开始学习模型。

export DATA_DIR=/path/to/data
export BERT_BASE_DIR=/path/to/bert_base

python run_pretraining.py \
  --input_file=$DATA_DIR/tf_examples.tfrecord \
  --output_dir=$BERT_BASE_DIR \
  --do_train=True \
  --do_eval=True \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --train_batch_size=32 \
  --max_seq_length=128 \
  --max_predictions_per_seq=20 \
  --num_train_steps=1000000 \
  --num_warmup_steps=100000 \
  --learning_rate=1e-4 \
  --save_checkpoints_steps=200000

我们训练了100万步的模型。在Tesla K80 GPU上,完成此过程大约需要20天。但是,我们提供了在0.8百万步时的快照,因为它在下游分类任务中产生更好的结果。

下游分类任务

XNLI

XNLI 是用于评估跨语言推理分类任务的数据集。开发和测试集包含15种语言,数据经过了彻底编辑。还提供了机器翻译版本的训练数据。

可以通过使用已翻译为泰语的训练数据将仅限泰语预训练的BERT模型应用于XNLI任务。需要删除训练数据中单词之间的空格,以使其与预训练步骤中的输入一致。与泰语相关的XNLI处理文件可以下载 here

然后,可以使用此脚本学习XNLI任务。

export BPE_DIR=/path/to/bpe
export XNLI_DIR=/path/to/xnli
export OUTPUT_DIR=/path/to/output
export BERT_BASE_DIR=/path/to/bert_base

python run_classifier.py \
  --task_name=XNLI \
  --do_train=true \
  --do_eval=true \
  --data_dir=$XNLI_DIR \
  --vocab_file=$BPE_DIR/th.wiki.bpe.op25000.vocab \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --init_checkpoint=$BERT_BASE_DIR/model.ckpt \
  --max_seq_length=128 \
  --train_batch_size=32 \
  --learning_rate=5e-5 \
  --num_train_epochs=2.0 \
  --output_dir=$OUTPUT_DIR \
  --xnli_language=th \
  --spm_file=$BPE_DIR/th.wiki.bpe.op25000.model

以下表格比较了仅限泰语模型与XNLI基线和同时使用翻译数据进行训练的多语言Cased模型。

XNLI Baseline BERT
Translate Train Translate Test Multilingual Model Thai-only Model
62.8 64.4 66.1 68.9

Wongnai评论数据集

Wongnai评论数据集收集了来自 Wongnai 网站的餐厅评论和评分。任务是将评论分类为五个评级(1至5星)。可以下载数据集 here ,并可以运行以下脚本使用仅限泰语模型进行此任务。

export BPE_DIR=/path/to/bpe
export WONGNAI_DIR=/path/to/wongnai
export OUTPUT_DIR=/path/to/output
export BERT_BASE_DIR=/path/to/bert_base

python run_classifier.py \
  --task_name=wongnai \
  --do_train=true \
  --do_predict=true \
  --data_dir=$WONGNAI_DIR \
  --vocab_file=$BPE_DIR/th.wiki.bpe.op25000.vocab \
  --bert_config_file=$BERT_BASE_DIR/bert_config.json \
  --init_checkpoint=$BERT_BASE_DIR/model.ckpt \
  --max_seq_length=128 \
  --train_batch_size=32 \
  --learning_rate=5e-5 \
  --num_train_epochs=2.0 \
  --output_dir=$OUTPUT_DIR \
  --spm_file=$BPE_DIR/th.wiki.bpe.op25000.model

在没有额外的预处理和进一步微调的情况下,仅限泰语的BERT模型在公共和私人测试集分数上分别达到了0.56612和0.57057。