自然言語処理初心者向け実践Tips

自然言語処理のために便利な、基本的なUNIXコマンドについての記事です。少しだけPythonの話もあります。

強い人は当然知っていて、しかも細かい（実践）知識というのは、初心者は知りにくいです。なぜかというと調べ方も何が問題かすらもよく分からないから。そういうわけで、誰かから教えてもらわないと辿り着くまでに時間がかかりそうで、知っておくと仕事が捗りそうな知識を並べています。

ちなみにタイトルに「初心者向け」とは書きましたが、より正確には「自然言語処理のお勉強を少しはしたけど実践が伴っていなくて、まだUNIXコマンドやPythonに詳しくない人向け」です。この記事は研究室に入ってしばらく経った後輩向けに作ったスライド資料を基にしているので、そういう対象になっています。


##データの大きさを知りたい
よく「そのデータはどれくらいの量なの」と聞かれることがあります（というか聞かれなくても自分で知っておくべき）。それを知るためにいちいちPythonでコードを書くのは労力の無駄遣いなので、Unixコマンドを使ったほうがいいです。

自然言語処理の場合、データの大きさと言えば「文の数」「トークン（単語）の数」「タイプ（語彙）の数」です。「文書の数」も入ったりしますが、かなり場合によりけりなので今回は考えないことにします。今回は1文1行に書かれたテキストファイルがあるとして、それについて調べます。

### 行数と単語数
次のようなコーパスがあるとします。

```
$ cat corpus.txt 
Colorless green ideas sleep furiously
Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo
That that is is that that is not is not is that it it is
```

この行数、単語数、文字数について調べたいなら`wc`コマンドを使えばいいです。

```
$ wc corpus.txt 
  3  28 159 corpus.txt
```
1列目: 行数, 2列目: 単語数（トークン数）, 3列目: 文字数
ただ行数だけ知りたい場合、単語数だけ知りたい場合、文字数だけ知りたい場合があり、そういうときはオプションで欲しいものだけ指定します。

```
$ wc -l corpus.txt 
3 corpus.txt
```
オプション `-l`, `--lines` で行数だけを表示
1文1行のファイルなら、これで文の数が分かります。

```
$ wc -w corpus.txt 
28 corpus.txt
```
オプション `-w`, `--words` で単語数（トークン数）だけを表示

```
$ wc -m corpus.txt 
159 corpus.txt
```
オプション `-m`, `--chars` で文字数だけを表示
（文字数だけ知りたいという場面はほとんど無いと思いますが一応）

### 語彙サイズ
語彙サイズ（単語の異なり数）を知りたいときですが、多少込み入った処理をする必要があります（なかったらごめんなさい）。込み入った処理をするならPerlを使うできでは？という気がしますが、自分はPerlに詳しくないのでUNIXコマンドをパイプで繋げてやることにします。

（まぁ実際のプログラム上では頻度いくらか以下の単語は`<UNK>`とするみたいな処理が入るので、以下で述べるのは練習以上の意味はないかもしれませんが……）

```
$ cat corpus.txt | sed 's/ /\n/g' | sort | uniq
Buffalo
Colorless
That
buffalo
furiously
green
ideas
is
it
not
sleep
that
```
`sed 's/ /\n/g'`で半角スペースを改行文字に置換（＝1行1単語形式に変換）し、`sort | uniq`で並び替え・重複除去をしています。この出力が語彙セットになります。

```
$ cat corpus.txt | sed 's/ /\n/g' | sort | uniq | wc -l
12
```
最後に`wc -l`すると行数（語彙数）が12だと分かります。

## 不都合な文字コードへの対応
### 文字コードの変換
日本語のデータとして青空文庫は割と便利なのですが、まずいことに文字コードがShift-JISです。UNIXやPythonで処理するには不都合なので、文字コードをutf-8に変換します。

文字コードの変換には`nkf`を使えばいいです。たぶんデフォルトでは入っていないコマンドなので、インストールする必要はあります。

例として、まず青空文庫から『吾輩は猫である』を落としてきて、中身（最初の10行）を確認します。

```
$ head wagahaiwa_nekodearu.txt 
���y�͔L�ł���
�Ėڟ���

-------------------------------------------------------
�y�e�L�X�g���Ɍ������L���ɂ��āz

�s�t�F���r
�i���j���y�s�킪�͂��t

�b�F���r�̕t���������̎n�܂������肷���L��
```

すると上のように文字化けします。

```
$ nkf -g wagahaiwa_nekodearu.txt 
Shift_JIS
```

オプション `-g, --guess` で文字コードを推測
コードはShift-JISらしいと分かりました。

```
$ nkf -w wagahaiwa_nekodearu.txt | head
吾輩は猫である
夏目漱石

-------------------------------------------------------
【テキスト中に現れる記号について】

《》：ルビ
（例）吾輩《わがはい》

｜：ルビの付く文字列の始まりを特定する記号
```

オプション `-w` で utf-8 に変換
オプション `--overwrite` でファイルを上書き

これで文字化けせずに正しく表示できました。

### Pythonでのファイル読み込み時のエラー
たまに文字コードを utf-8 にしても Python がデコードできないと言い出すことがあります。

```
UnicodeDecodeError: 'utf-8' codec can't decode byte XXXX in position YYYY: 
```

こういうときは仕方がないので読み込み時にエラーのときの対応を指定します。

```
open(fname, 'r' , errors='ignore')
open(fname, 'r' , errors='replace')
```
`ignore` を指定するとデコードできない文字を無視します。
`replace` だと`�`に置換します。

## ワイルドカード
```
$ ls aozora/
bocchan.txt  kusamakura.txt  wagahaiwa_nekodearu.txt

$ nkf -w --overwrite aozora/*.txt

$ head -2 aozora/*.txt
==> aozora/bocchan.txt <==
坊っちゃん
夏目漱石

==> aozora/kusamakura.txt <==
草枕
夏目漱石

==> aozora/wagahaiwa_nekodearu.txt <==
吾輩は猫である
夏目漱石
```

`*` がワイルドカードです。一括処理できるので便利です。


## さいごに
不慣れな人はUNIXコマンドを多少使えるようになれば何かと楽になるかと思います。

何かと変なところがあれば教えてください。