にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

CNNによる文字コード不明なドキュメントの推定

CNNによる文字コード不明なドキュメントの推定

Advent Calender遅刻いい訳

  1. 年末忙しすぎた
  2. ネタと期待していたいくつかがまともに結果が出ずに苦しい思いをしていた
  3. 元URLの喪失

バイト列から文字コーディングを推定する

Twitterで時々バズるネタとして、機械学習がこれほどもてはやされるのに、今だにBrowserは時々文字化けし、ExcelはUTF8を突っ込むと文字化けし、到底、文化的で最低限の人権が保護された状態ではありません。

実際、ルールベースで推定しようとすると、この様にshift jisとeucでは完全に背反な情報を使っているわけでないので、なんらかのヒューリスティックなルールを人間が作成して対応していたのだと思いますが、この様なユースケースの場合、機械学習が強い力を発揮します。

図1. sjisとeucの文字コードのバイト列のマップ(参考:smdn)

その度、「それ、できると思うよ」って言い返していたのですが、実証実験を行いたいと思います。

なんの機械学習アルゴリズムがいいか

ニュースサイトをスクレイピングすると、大量のUTF8のテキスト情報が取得できます

このテキスト情報をもとに、nkfというコマンドで、euc, sjisの文字コードに変換して、様々な文字コードのバージョンを作ります

Pythonやいくつかの言語では、UTF8以外を扱うとバグるのですが、バイト列としてみなすと読み込みが可能になり、バイト列にはなんらかの特徴が見て取れそうです(仮説)

バイト列をベクトル化して、CNNのテキスト分類の機械学習で分類することが良さそうです

ネットワーク

VGGのネットワークを参考に編集しました。

図2. 作成したネットワーク

目的関数

微妙な判断結果になった場合、確率を正しく出力したいので、sotfmaxではなく、3つのsigmoidを出力して、それぞれのbinary cross entropyを損失としています

出力の解釈性が良いので個人的によく使うテクニックです

コード

全体のコードはgithubにあります

CBRDという関数はosciiartさんの作り方を参考にさせていただきました

def CBRD(inputs, filters=64, kernel_size=3, droprate=0.5):
  x = Conv1D(filters, kernel_size, padding='same',
            kernel_initializer='random_normal')(inputs)
  x = BatchNormalization()(x)
  x = Activation('relu')(x)
  return x

input_tensor = Input( shape=(300, 95) )

x = input_tensor
x = CBRD(x, 2)
x = CBRD(x, 2)
x = MaxPool1D()(x)

x = CBRD(x, 4)
x = CBRD(x, 4)
x = MaxPool1D()(x)

x = CBRD(x, 8)
x = CBRD(x, 8)
x = MaxPool1D()(x)

x = CBRD(x, 16)
x = CBRD(x, 16)
x = CBRD(x, 16)
x = MaxPool1D()(x)

x = CBRD(x, 32)
x = CBRD(x, 32)
x = CBRD(x, 32)
x = MaxPool1D()(x)

x = Flatten()(x)
x = Dense(3, name='dense_last', activation='sigmoid')(x)
model = Model(inputs=input_tensor, outputs=x)
model.compile(loss='binary_crossentropy', optimizer='adam')

データセット

nifty newsさんとniconico newsさんのニュースコーパスを利用しました。

zipファイルを分割して圧縮しています

もし、お手元で試していただいて性能が出ないと感じる場合は、おそらく、コーパスの属性があっていないものですので、再学習してもいいと思います

https://github.com/GINK03/keras-cnn-character-code-detection/tree/master/dataset

前処理

dbmに入ったデータセットから内容をテキストファイルで取り出します

$ python3 14-make_files.py

nkfを使ってeucのデータセットを作成します(Python2で実行)

$ python2 15-make_euc.py

nkfを使ってsjisのデータセットを作成します(Python2で実行)

$ python2 16-make_shiftjis.py

byte表現に対してindexをつけます(Python3で実行)

$ python3 17-unicode_vector.py 

最終的に用いるデータセットを作成してKVSに格納します(LevelDBが必要)

$ python3 18-make_pair.py

学習

$ python3 19-train.py --train

テストデータにおける精度

hash値でデータを管理していて、7から始まるデータをテストデータしています

Train on 464 samples, validate on 36 samples
Epoch 1/1
464/464 [==============================] - 1s 1ms/step - loss: 2.1088e-05 - val_loss: 2.8882e-06

val_lossが極めて小さい値になっており、十分小さい値を出しています

精度

7から始まるhash値のデータセットで1000件検証したところ、99.9%でした(すごい)

$ python3 19-train.py --precision 
actual precision 99.9

予想

$ python3 19-train.py --predict --file=${FILE_PATH}

例

$ python3 19-train.py --predict --fild=
$ python3 19-train.py --predict --file=../keras-mojibake-grabled/eucs/000000123.txt 
Using TensorFlow backend.
this document is EUC. # <- EUCとして判別された

終わりに

モデルのサイズ自体は、151kbyteとかなりコンパクトに収まっていて、精度自体も実践的です。

Microsoft Excelなどで文字コードが判定されなく化けていていて、毎回、数分損失するので、ネットワーク自体は深いですが、軽量なので組み込んで利用することも可能かと思います。

このように、実際に機会学習を適応して、生活が豊かになると良いですね。