8
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

pythonのcursesを使ってBPEをインタラクティブに出力

Posted at

TUI (テキストユーザーインタフェース) を作成するためのライブラリcursesを利用して、BPEの学習経過をいい感じに出力します。

全体のコードはgistにアップロードしています。
:arrow_right: bpe_curses.py

環境

  • macOS Catalina
  • Python3.7.3
  • iTerm2 (3.3.7)

BPE

(cursesについてだけ知りたい人は読み飛ばしてください)

BPEとは

バイトペア符号化(Byte Pair Encoding)はニューラル機械翻訳のトークナイザであるSentencepieceにも用いられている手法です。
初出は Neural Machine Translation of Rare Words with Subword Units (ACL2016) で、実装も論文中に記載されています。

例えば、 lower, newer, wider のような単語があった場合、頻度の高い e r を一つのシンボル er として扱うことで、語彙数を削減できます。

このように、NLPではサブワード分割アルゴリズムとして有名ですが、そもそもはデータ圧縮手法であり、バイト対符号化 (Wikipedia) にもその原理が紹介されています。

今回は、論文のコードに基づいて、その圧縮経過を出力します。

BPE実装の概要

BPEの実装については、論文のコードをそのまま利用し、型ヒントを追加するだけに留めています。

実装は大きく二つ、関数 get_status()merge_vocab() で成り立っています。

  • get_status はvocab辞書を受け取り、wordの組み合わせの頻度を調べます。
    • defaultdictを使っているのは、keyにない組み合わせも扱うためです。
  def get_stats(vocab: Dict) -> DefaultDict:
    pairs = collections.defaultdict(int)
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
            pairs[symbols[i], symbols[i+1]] += freq
    return pairs
  • merge_vocab では、get_statusで調べた組み合わせの中で、最も頻度の高い組み合わせを、ひとつのwordとして扱うためにmergeします。
def merge_vocab(pair: List, v_in: Dict) -> Dict:
    v_out = {}
    bigram = re.escape(' '.join(pair))
    p = re.compile(r'(?<!<\S)' + bigram + r'(?!\S)')
    for word in v_in:
        w_out = p.sub(''.join(pair), word)
        v_out[w_out] = v_in[word]
    return v_out

今回は merge_vocab 後の単語の状態遷移を表示することにします。

Curses

Curses とは

curses ライブラリは、VT100s や Linux コンソール、さまざまなプログラムが提供するエミュレーション端末といったテキストベースの端末(ターミナル)のために、端末に依存しないスクリーン描画や、キーボードの処理を提供します。
Python で Curses プログラミングより

cursesはpythonの標準モジュールです。(windows版には含まれていないようですが。。)
cursesを利用すれば、簡単にCUIアプリっぽいものが作れます。

例えば、pythonのdemoにあるlife.pyはターミナル上でライフゲームができるコードです。
lifepy.gif

Cursesの使い方

cursesで状態遷移の表示を実装していきます。

wrapper を使おう

Python で Curses プログラミング で説明されている通り、エラー処理や初期化の煩雑さを避けるため、curses.wrapper() 関数を使います。

import curses

def main(stdscr):
	# stdscrでcursesの処理を呼び出す

if __name__ == '__main__':
    curses.wrapper(main)

基本的な動作

基本的な処理の流れは以下のようになります。

  • stdscr.addstr(str): 現在の位置にテキスト str を追加
  • stdscr.refresh(): ディスプレイを更新
    • addstr したテキストを表示する
  • stdscr.getkey(): キー入力を受け付ける
    • 待ち状態の役割(ないとプログラムがそのまま終了する)
for i in range(10):
    stdscr.addstr('{}\n'.format(i))
    stdscr.refresh()
    stdscr.getkey()

上記のようなコードの場合は数字を表示して待ち状態を繰り返します。

画面からのはみ出しエラーを防ぐ

画面の高さよりも長い範囲に表示しようとすると、エラーになります。

エラーを防ぐためには、最初に現在のディスプレイの大きさを取得しておいて、ディスプレイの範囲外を指定しないよう工夫が必要です。 getmaxyx() で大きさを取得できます。

stdscr_y, stdscr_x = stdscr.getmaxyx()

表示の工夫

そのままだと味気ないので、表示を工夫してみます。

今回は文字をmergeした場合、単語を太字にします。
具体的には、addstr に属性情報 curses.A_BOLD を追加します。

他にも、色をつけたり、点滅させたりもできます。実際の、属性とカラー の属性と実行結果は以下のようになります。

stdscr.addstr('This is A_BOLD\n', curses.A_BOLD)
stdscr.addstr('This is A_BLINK\n', curses.A_BLINK)
stdscr.addstr('This is A_DIM\n', curses.A_DIM)
stdscr.addstr('This is A_STANDOUT\n', curses.A_STANDOUT)
stdscr.addstr('This is A_REVERSE\n', curses.A_REVERSE)
stdscr.addstr('This is A_UNDERLINE\n\n', curses.A_UNDERLINE)
# 背景と文字色を指定する
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
stdscr.addstr("This is curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)\n", curses.color_pair(1))

※表示がわかりやすいように、結果出力のiTermの設定を Dark Background にしています。

出力結果

以上を加味して作成したbpe_curses.pyを実行すると、以下のように、キーを叩くたびにmergeして更新された結果が出力されます。

bpe_sample.gif

文字が小さくて少しわかりにくいですが、 太字 がmergeされたペアが含まれる単語です。最初は er が最頻なペアのため、newerwider が太字になっています。(2行目)

また、10回mergeを繰り返すことで、一番左に表示されている語彙数が減っているのがわかります。(14→6)

今回インタラクティブである必要性があまりなかった気がしますが、cursesはキー入力を受け取ったりできるので、工夫次第では様々な活用できる気がします。
GUIを実装するより楽なので、ちょっとした出力を見せるのに良いかもしれません。

参考

8
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?