素人の言語処理100本ノック:81


[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)の挑戦記録です。環境はUbuntu 16.04 LTS ＋ Python 3.5.2 \:\: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧は[こちら](http://qiita.com/segavvy/items/fb50ba8097d59475f760)からどうぞ。

## 第9章: ベクトル空間法 (I)
>[enwiki-20150112-400-r10-105752.txt.bz2](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/enwiki-20150112-400-r10-105752.txt.bz2)は，2015年1月12日時点の英語のWikipedia記事のうち，約400語以上で構成される記事の中から，ランダムに1/10サンプリングした105,752記事のテキストをbzip2形式で圧縮したものである．このテキストをコーパスとして，単語の意味を表すベクトル（分散表現）を学習したい．第9章の前半では，コーパスから作成した単語文脈共起行列に主成分分析を適用し，単語ベクトルを学習する過程を，いくつかの処理に分けて実装する．第9章の後半では，学習で得られた単語ベクトル（300次元）を用い，単語の類似度計算やアナロジー（類推）を行う．
>
>なお，問題83を素直に実装すると，大量（約7GB）の主記憶が必要になる． メモリが不足する場合は，処理を工夫するか，1/100サンプリングのコーパス[enwiki-20150112-400-r100-10576.txt.bz2](http://www.cl.ecei.tohoku.ac.jp/nlp100/data/enwiki-20150112-400-r100-10576.txt.bz2)を用いよ．

### 81. 複合語からなる国名への対処
>英語では，複数の語の連接が意味を成すことがある．例えば，アメリカ合衆国は"United States"，イギリスは"United Kingdom"と表現されるが，"United"や"States"，"Kingdom"という単語だけでは，指し示している概念・実体が曖昧である．そこで，コーパス中に含まれる複合語を認識し，複合語を1語として扱うことで，複合語の意味を推定したい．しかしながら，複合語を正確に認定するのは大変むずかしいので，ここでは複合語からなる国名を認定したい．
>
>インターネット上から国名リストを各自で入手し，80のコーパス中に出現する複合語の国名に関して，スペースをアンダーバーに置換せよ．例えば，"United States"は"United_States"，"Isle of Man"は"Isle_of_Man"になるはずである．

#### 出来上がったコード：

```python:main.py
# coding: utf-8
fname_input = 'corpus80.txt'
fname_output = 'corpus81.txt'
fname_countries = 'countries.txt'

# 国名一覧を読み込んで集合と辞書作成、ただし1語の国は含めない。
# 辞書には{ 最初の1語, [全体の語数1, 全体の語数2...] }を登録し、
# 全体の語数は降順でソートして格納する。
# たとえば最初の1語が'United'の国は次の6つある。
#	United States of America
#	United Mexican States
#   United Kingdom of Great Britain and Northern Ireland
#	United Arab Emirates
#   United Republic of Tanzania
#   United States
# この場合、全体の語数が4語、3語、8語、2語のものがあるので、
# 辞書には { 'United', [8, 4, 3, 2] } を登録する。
# 全体の個数を降順ソートするのは最長一致でマッチングさせるため。
set_country = set()
dict_country = {}
with open(fname_countries, 'rt') as data_file:
	for line in data_file:
		words = line.split(' ')
		if len(words) > 1:

			# 集合に追加
			set_country.add(line.strip())

			# 辞書に追加
			if words[0] in dict_country:
				lengths = dict_country[words[0]]
				if not len(words) in lengths:
					lengths.append(len(words))
					lengths.sort(reverse=True)
			else:
				dict_country[words[0]] = [len(words)]

# 1行ずつ処理
with open(fname_input, 'rt') as data_file, \
		open(fname_output, mode='wt') as out_file:
	for line in data_file:

		# 1語ずつチェック
		tokens = line.strip().split(' ')
		result = []		# 結果のトークン配列
		skip = 0		# >0なら複数語の続き
		for i in range(len(tokens)):

			# 複数語の続きの場合はスキップ
			if skip > 0:
				skip -= 1
				continue

			# 1語目が辞書にある？
			if tokens[i] in dict_country:

				# 後続の語数を切り取って集合にあるかチェック
				hit = False
				for length in dict_country[tokens[i]]:
					if ' '.join(tokens[i:i + length]) in set_country:

						# 複数語の国を発見したので'_'で連結して結果に追加
						result.append('_'.join(tokens[i:i + length]))
						skip = length - 1		# 残りの語はスキップ
						hit = True
						break
				if hit:
					continue

			# 複数語の国ではないので、そのまま結果に追加
			result.append(tokens[i])

		# 出力
		print(*result, sep=' ', end='\n', file=out_file)
```
#### 実行結果：

結果ファイル「orpus81.txt」で変換されている部分の抜粋です。

```console:corpus81.txtの抜粋
The largest fleet of these types is operated by the United_States Navy including the dating back to the 1970s and the larger ships that debuted in 1989 Amphibious assault ships are also operated by the British Royal Navy the French Navy the Italian Navy the Republic_of_Korea Navy and the Spanish Navy.
```

全体は[GitHub](https://github.com/segavvy/nlp100_Python/tree/master/81)にアップしています。

### 国名リスト
国名リスト「countries.txt」は、国名を改行区切りで列挙したテキストファイルです。外務省のサイトにある[「KIDS外務省 - 世界の国々」](http://www.mofa.go.jp/mofaj/kids/ichiran/index.html)から作成しました。6地域に分かれていますが、国名一覧に「英語による名称」があります。
Webサイトの表は任意の列だけをコピーすることができないのですが、表全体をコピーしてExcelに貼り付けると「英語による名称」の列だけを簡単に取り出すことができます。

ただしこの表には、問題文の例にある「United State」と「Isle of Man」がありません。「United State」は「United State of America」になっています。「Isle of Man」は英国と同じ扱いのようです。ただ問題の例にあるものがその通りに変換できないのも良くなさそうなので、この2つは最後に追加しておきました。

```console:countries.txtの先頭部分
India
Republic of Indonesia
Kingdom of Cambodia
Republic of Singapore
Democratic Socialist Republic of Sri Lanka
Kingdom of Thailand
Republic of Korea
People's Republic of China
Japan
Federal Democratic Republic of Nepal
Islamic Republic of Pakistan
People's Republic of Bangladesh
The Democratic Republic of Timor-Leste
Republic of the Philippines
Kingdom of Bhutan
Brunei Darussalam
Socialist Republic of Viet Nam
Malaysia
Republic of the Union of Myanmar
Republic of Maldives
（以下略）
```

全体は[GitHub](https://github.com/segavvy/nlp100_Python/tree/master/81)にアップしています。

### 国名の変換
変換をなるべく高速に行うため、国名の1語目だけの辞書を作成しています。1語目が該当する場合のみ、後続の言葉が国名になっているかをチェックしています。

　
82本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
<hr>

*実行結果には、[100本ノックで用いるコーパス・データ](http://www.cl.ecei.tohoku.ac.jp/nlp100/#data)で配布されているデータの一部が含まれます。
この第9章で用いているデータのライセンスは[クリエイティブ・コモンズ 表示-継承 3.0 非移植](https://creativecommons.org/licenses/by-sa/3.0/legalcode)（[日本語訳](https://creativecommons.org/licenses/by-sa/3.0/deed.ja)）です。
国名一覧「countries.txt」は、「KIDS外務省 - 世界の国々」（外務省）（[http://www.mofa.go.jp/mofaj/kids/ichiran/index.html](http://www.mofa.go.jp/mofaj/kids/ichiran/index.html)）を加工して作成しています。*