7
5

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 3 years have passed since last update.

TensorFlow2.0Advent Calendar 2019

Day 7

Mecabとtf.dataを使ってlivedoorニュースコーパスを分かち書きする

Last updated at Posted at 2019-12-06

#はじめに
@Suguru_Toyoharaさんがtf.data.Datasetの使い方をわかりやすくまとめていただいていますが、このエントリでは自然言語処理でよくやるコーパスを分かち書きしてデータセットを作る、というフローをtd.dataを使って行います。
#livedoor コーパス
livedoorコーパスはNHN Japan株式会社が運営する「livedoor ニュース」のうち、下記のクリエイティブ・コモンズライセンスが適用されるニュース記事を収集したもので、こちらのリンクからダウンロードができます。
https://www.rondhuit.com/download.html

このページのldcc-20140209.tar.gzをダウンロードして解凍します。

$wget https://www.rondhuit.com/download/ldcc-20140209.tar.gz
$ tar zfx ldcc-20140209.tar.gz

解凍したファイルは以下のようなディレクトリ構造になっています。

textの中身
$tree text -L 1
text
├── CHANGES.txt
├── dokujo-tsushin
├── it-life-hack
├── kaden-channel
├── livedoor-homme
├── movie-enter
├── peachy
├── README.txt
├── smax
├── sports-watch
└── topic-news
text/dokujo-tsushinの中身
# tree text/dokujo-tsushin
text/dokujo-tsushin
├── dokujo-tsushin-4778030.txt
├── dokujo-tsushin-4778031.txt
├── dokujo-tsushin-4782522.txt
├── dokujo-tsushin-4788357.txt
...
├── dokujo-tsushin-6915005.txt
└── LICENSE.txt

#分かち書きデータセット作成
解凍したコーパスをもとにtf.dataMecabを使ってテキストの分かち書きを行っていきたいと思います。
※Mecab本体はインストール済みの状態とします。

fugashiインストール

mecab-python3はメンテされていないとのことなので、今回はMecabラッパーにfugashiを選択しました。

$ pip install fugashi

データセット作成

tensorflow_datasetsfugashiなどを読み込み、Mecabのtaggerを生成します。

import os
import tensorflow as tf
import tensorflow_datasets as tfds
import fugashi

tagger = fugashi.Tagger('-Owakati')

Tensorflowではテキストファイルのコーパスを操作するAPIが数多く揃っており、tf.compat.v1.gfile.ListDirectoryを使うと指定したディレクトリ配下のリストを取得できます。

livedoorコーパス内のファイルとディレクトリ一覧を取得
text_dir = os.path.join(os.getcwd(),"text")
tf.compat.v1.gfile.ListDirectory(text_dir)
['CHANGES.txt',
 'dokujo-tsushin',
 'it-life-hack',
 'kaden-channel',
 'livedoor-homme',
 'movie-enter',
 'peachy',
 'README.txt',
 'smax',
 'sports-watch',
 'topic-news']

tf.compat.v1.gfile.ListDirectoryをネストしてファイル一覧を取得し、tf.data.TextLineDataset APIを使って各カテゴリ内のファイルを取得します。

データセット作成
text_datasets = []
text_dir = os.path.join(os.getcwd(),"text")
for d in tf.compat.v1.gfile.ListDirectory(text_dir):
    data_dir = os.path.join(text_dir,d)
    if os.path.isdir(data_dir):
        for file_name in tf.compat.v1.gfile.ListDirectory(data_dir):
            text_dataset = tf.data.TextLineDataset(os.path.join(data_dir,file_name))
            text_datasets.append(text_dataset)
 

作成したデータセットの中身を見てみましょう。

plane_dataset = text_datasets[0]
iter_plane_dataset = iter(plane_dataset)
print(next(iter_plane_dataset).numpy().decode('utf-8'))
print(next(iter_plane_dataset).numpy().decode('utf-8'))
print(next(iter_plane_dataset).numpy().decode('utf-8'))
http://news.livedoor.com/article/detail/4778030/
2010-05-22T14:30:00+0900
友人代表のスピーチ、独女はどうこなしている?

tf.compat.v1.gfile.ListDirectorytf.data.TextLineDatasetを組み合わせることで、コーパスファイル群から簡単にデータセットを作成することができました。

この手順だとtext_datasets[0]にはdokujo-tsushin-4778030.txtのテキストが、text_datasets[1]にはdokujo-tsushin-4778031.txtというように、ファイル単位で分割されてしまうため、文章単位となるようにconcatenateする必要がありました。

parsed_dataset = text_datasets[0]
for text_dataset in text_datasets[1:]:
    parsed_dataset = parsed_dataset.concatenate(text_dataset)

これでデータセット作成の完了です。

分かち書きデータセットを作成

分かち書きデータセットを用意する場合、このデータセットに対してさらにfugashiのTaggerを当てる必要がありますが、tf.data.TextLineDatasetmapを使えば、データセット作成時にまとめて分かち書きすることができます。

分かち書きデータセット作成
def parse(text):
    '''
    データセットのテキストを1行ごとに分かち書き
    '''
    return tagger.parse(text.numpy().decode('utf-8')).split("\n")[0]

@tf.function
def parse_text(text):
    parsed = tf.py_function(parse,[text],[tf.string])
    return parsed[0]

text_datasets = []
text_dir = os.path.join(os.getcwd(),"text")
for d in tf.compat.v1.gfile.ListDirectory(text_dir):
    data_dir = os.path.join(text_dir,d)
    if os.path.isdir(data_dir):
        for file_name in tf.compat.v1.gfile.ListDirectory(data_dir):
            text_dataset = tf.data.TextLineDataset(os.path.join(data_dir,file_name)).map(parse_text) # mapでテキストをパース処理に渡す
            text_datasets.append(text_dataset)
            
parsed_dataset = text_datasets[0]
for text_dataset in text_datasets[1:]:
    parsed_dataset = parsed_dataset.concatenate(text_dataset)

今度は分かち書きされた結果がきちんと返っています。

parsed_dataset = text_datasets[0]
iter_parsed_dataset = iter(parsed_dataset)
print(next(iter_parsed_dataset).numpy().decode('utf-8'))
print(next(iter_parsed_dataset).numpy().decode('utf-8'))
print(next(iter_parsed_dataset).numpy().decode('utf-8'))
http :// news . livedoor . com / article / detail / 4778030 /
2010 - 05 - 22 T 14 : 30 : 00 + 0900
友人 代表 の スピーチ 、 独 女 は どう こなし て いる ?

テキストエンコード

機械学習で自然言語を扱う場合、分かち書き結果を個々のキーワードに分割し、数字にエンコードする必要があります。

Tensorflow 2.0のテキストチュートリアルを参考にtfds.features.text.Tokenizertfds.features.text.TokenTextEncoderを使ってテキストをエンコードしてみます。

まずはトークナイザを生成します。

トークナイザを生成
tokenizer = tfds.features.text.Tokenizer()

トークナイザはデフォルトで半角記号などを排除するので、それらが必要な場合は生成時にreserved_tokensで指定しておく必要があります。

reserved_tokensあり
tokenizer = tfds.features.text.Tokenizer(reserved_tokens=["/",":"])

生成したトークナイザを使い、分かち書きデータセットを分割してボキャブラリを構築します。

ボキャブラリ構築
vocabulary_set = set()
for line in parsed_dataset:
    some_tokens = tokenizer.tokenize(line.numpy())
    vocabulary_set.update(some_tokens)

ボキャブラリからエンコーダを生成します。

encoder = tfds.features.text.TokenTextEncoder(vocabulary_set, tokenizer=tokenizer)

結果を確認します。

sample_text = next(iter_parsed_dataset)
encoded_example = encoder.encode(sample_text.numpy().decode('utf-8'))

for enc in encoded_example:
    print("{} -> {}".format(enc, encoder.decode([enc])))
9058 -> もうすぐ
3331 -> ジューン
31397 -> ブライド
27220 -> と
2397 -> 呼ば
22334 -> れる
5673 -> 6月
31920 -> 独
32073 -> 女
20415 -> の
28229 -> 中
18191 -> に
986 -> は
8138 -> 自分
20415 -> の
10202 -> 式
986 -> は
36484 -> まだ
10551 -> な
26617 -> のに
2397 -> 呼ば
34 -> れ
27080 -> て
27726 -> ばかり
27509 -> という
6682 -> お祝い
903 -> 貧乏
5111 -> 状態
20415 -> の
35344 -> 人
4722 -> も
21766 -> 多い
20415 -> の
22018 -> で
986 -> は
31505 -> ない
12417 -> だろ
7199 -> う
23655 -> か
35100 -> さらに
22067 -> 出席
16090 -> 回数
18014 -> を
1399 -> 重ね
27080 -> て
27779 -> いく
27220 -> と
6523 -> こんな
6851 -> お願い
6769 -> ごと
18014 -> を
32709 -> さ
22334 -> れる
30766 -> こと
4722 -> も
3435 -> 少なく
31505 -> ない

作成したエンコーダを保存しておくことで、他のタスクにも使うことができます。

encoder.save_to_file('sample/encoded.model')

エンコードファイルの中身はこのようになっています。

### TokenTextEncoder
### Metadata: {"has_tokenizer": true, "lowercase": false, "oov_buckets": 1, "oov_token": "UNK"}
一番
相談
取り入れ
幸恵
ばかり
多い
\できる
よう
みる
どう
何
回数
なら
話す
いっ
あげれ
者
って
他
よ
サプライズ

保存したエンコーダはtfds.features.text.TokenTextEncoder.load_from_fileから呼び出すことができます。

loaded_encoder = tfds.features.text.TokenTextEncoder.load_from_file('sample/encoded.model')

encoded_example = loaded_encoder.encode(sample_text.numpy().decode('utf-8'))

for enc in encoded_example:
    print("{} -> {}".format(enc, encoder.decode([enc])))

保存前とワードのIDが変わっていないことが確認できます。

9058 -> もうすぐ
3331 -> ジューン
31397 -> ブライド
27220 -> と
2397 -> 呼ば
22334 -> れる
5673 -> 6月
31920 -> 独
32073 -> 女
20415 -> の
28229 -> 中
18191 -> に
986 -> は
8138 -> 自分
20415 -> の
10202 -> 式
986 -> は
36484 -> まだ
10551 -> な
26617 -> のに
2397 -> 呼ば
34 -> れ
27080 -> て
27726 -> ばかり
27509 -> という
6682 -> お祝い
903 -> 貧乏
5111 -> 状態
20415 -> の
35344 -> 人
4722 -> も
21766 -> 多い
20415 -> の
22018 -> で
986 -> は
31505 -> ない
12417 -> だろ
7199 -> う
23655 -> か
35100 -> さらに
22067 -> 出席
16090 -> 回数
18014 -> を
1399 -> 重ね
27080 -> て
27779 -> いく
27220 -> と
6523 -> こんな
6851 -> お願い
6769 -> ごと
18014 -> を
32709 -> さ
22334 -> れる
30766 -> こと
4722 -> も
3435 -> 少なく
31505 -> ない

まとめ

tf.datamapを組み合わせることで、簡単に分かち書きしたデータセットを作ることができます。

またtensorflow_datasetsのトークナイザとテキストエンコーダを使うことで、エンコーダも簡単に構築でき、自然言語の機械学習を行う際の前処理がすごく楽になるのでとてもよいと思います。

7
5
1

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
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?