Geminiの力を借りてコーパスから数万語レベルの英単語帳を自作する

## デカい英単語帳がない！
こんにちは。kokurenと申します。

見出しの通り、「デカい英単語帳がない！」です。
正直な話デカい英単語帳が必要なほど**僕は英語できない**です。

しかし、デカい英単語帳にはロマンがあります。

ネイティブの語彙力はだいたい3万語程度と言われているのにも関わらず、これだけ英語学習が盛んな日本において市販の英単語帳の語彙レベルはせいぜい10000～15000語程度です。

有名どころだとアルクが『SVL 究極の英単語』シリーズという合計12000語レベルの被りなし英単語帳を販売しています。

https://book.alc.co.jp/book/b10081100.html

個人の作成したものでは、"なりしかさん"という方が『究極の英単語』シリーズの次に学ぶべきリストとして、『極限の英単語』『終極の英単語』という35000語程度までのものをkindleで公開しています。kindle unlimitedにて無料で読めます。

https://www.amazon.co.jp/%E6%A5%B5%E9%99%90%E3%81%AE%E8%8B%B1%E5%8D%98%E8%AA%9E-Vol-1-15000%E8%AA%9E%E3%83%AC%E3%83%99%E3%83%AB-%E3%81%AA%E3%82%8A%E3%81%97%E3%81%8B-ebook/dp/B074MD9QCB

僕の知る限りではこのなりしかさんという方が出している極限・終極シリーズを除けば『ANC単語頻度準拠_英和辞典』の30000語のリストぐらいかと思います。
あとはCOCA60kなどでしょうか。

https://www.jamsystem.com/ancdic/sortable2/ancdic.html

ですが、個人的な印象としてはANC30kに関しては固有名詞の多さや、どの順番で学ぶべきかなどの使いにくさが感じられました。COCA60kはリストを入手するのに200ドル程度かかります。

なりしかさんのシリーズに関してはリストとしては相当よく作られているのですが、kindle形式かつ例文などはないのでそのままではとっつきにくく感じられました。

何かとっつきやすく大規模な英単語のリストはないものか…。
どうせなら例文や音声ファイルも欲しい！

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/2b595c9a-0edc-7db3-cc0b-09b3817540b2.png)

というわけで実際に作ってみたのでその手順や実際のリストを公開したいと思います。
他の言語や特定のドメインのコーパスにも応用できるかと思います。

## コーパスと単語の選定
英単語リストを作成するにあたって、まずは単語の選定をしなければなりません。
今回は以下の要件を考慮して単語を選定しました。

- 十分量のコーパスから選定されていること
- コーパス内での登場頻度を重視すること
- 固有名詞（人名や地名など）はできるだけ省くこと

各工程にはpythonの自然言語処理ライブラリであるNLTKやGemini APIを用いました。
細かい言語処理スクリプトの部分については省くのですが、Gemini及びLLMを使って、語学学習のためにこんなこともできるよというのをメインで紹介していこうと思います。

ついでに英語の音声合成の話もしちゃいます。

### コーパスって何？

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/56c43e18-4413-d560-8fc9-bbfc703925ea.png)

コーパスとは、**特定の言語で実際に使用されている言葉を集めた大規模なデータベース**のことです。簡単に言ってしまえば、**大量の特定言語のテキスト群**のことです。

British National Corpus (BNC)や、Corpus of Contemporary American English (COCA)など、さまざまなものがあり、言語学的な研究用途や、昨今ではLLMの開発などにも（多分）使われています。

今回はオープンソースかつそこそこの規模で、利用制限のない**Open American National Corpus**を選択しました。これは話し言葉から書き言葉までの様々なテキストデータを含む、1500万語規模のコーパスです。

今回はこの**OANC**を用いて頻度順の英単語リストを作成、その後固有名詞を省きつつ難易度ランクをつけ、最終的に英語例文とその和訳、さらにその読み上げ音声ファイルまでを作成しました。

最終的には以下のようなリストが作成されました。

```csvs
"word","CEFR","Japanese","Example","Example_JP"
"know","A1","知っている","I know the answer.","私は答えを知っている。"
"grandfather","A1","祖父","My grandfather is very old.","私の祖父はとても年をとっている。"
"sleeping","A1","眠っている","The cat is sleeping on the sofa.","猫はソファーで眠っている。"
"saturday","A1","土曜日","We are going to the park on Saturday.","私たちは土曜日に公園に行く予定です。"
"ants","A1","アリ","There are ants in the kitchen.","台所にアリがいる。"
```

CSV形式で保存しているので、そのままAnkiやQuizletなどのフラッシュカードアプリにインポートすることもできると思います。

※全リストや音声ファイルは記事の最後にgithubリンクを掲載いたします

### NLTKによる頻度順リストの作成
今回はこの部分に関しては最小限の説明に留めます。
（きっとChatGPTなどに聞いた方が早いと思います。）

NLTKとは、Natural Language Toolkitの略で、いわゆる自然言語処理のためのライブラリです。今回はこのライブラリを用いて、ストップワードの除外などをした上でOANC内の頻度順英単語リストを作成しました。

以下は該当部分のサンプルコードです。

```py
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
    text = f.read()
    # テキストを小文字に変換し、トークン化
    tokens = word_tokenize(text.lower())
    # 英単語と数字のみを抽出
    words = [re.sub(r'[^a-zA-Z0-9]', '', token) for token in tokens]
    # 空の文字列やストップワードを除外
    filtered_words = [word for word in words if word and word not in stop_words and len(word) > 2]
    word_counts.update(filtered_words)
```

この処理の結果、以下のようなCSVファイルが得られます。
今回は単語のコーパス内での登場回数を数え、それを頻度として保存しました。

```csvs
know,51699
yeah,46028
one,39859
like,34368
well,30570
```

これだけでも価値のあるリストといえばそうなのですが、このままでは固有名詞（地名や人名）や会話における相づち（uhhuhみたいなの）などが多数含まれます。ここでGeminiの出番がやってきます。

## Geminiで単語リストを処理する
### 固有名詞を弾きつつ難易度ランクをつける
上記のリストから今回は6回以上登場した単語に絞り、この段階では50000語程度ありました。しかし、前述の通り、単語と呼んでいいのかわからない表現や固有名詞などが多く含まれる状態です。

こういった語彙をなるべく削除したいのですが、5万行のリストを一つずつ確認していては日が暮れるどころではありません。

さらにわがままを言うなら、これはあくまでもコーパス内での頻度順のリストであって、頻度=難易度ではないということです。できることならCEFRに即した難易度順で並べ替えられたら嬉しいでしょう。

https://ja.wikipedia.org/wiki/%E3%83%A8%E3%83%BC%E3%83%AD%E3%83%83%E3%83%91%E8%A8%80%E8%AA%9E%E5%85%B1%E9%80%9A%E5%8F%82%E7%85%A7%E6%9E%A0

そこで、100行ごとに以下のプロンプトでgemini-2.0-flash-expにリクエストを送り、余計な表現を弾きつつ、難易度付けを行いました。

```py
PROMPT_TEMPLATE = """
以下は英単語のリストです。各単語に対して、以下のCEFR（Common European Framework of Reference for Languages）の6段階評価（A1, A2, B1, B2, C1, C2）のレベルを付けてください。
固有名詞や単語ではない表現、特殊な文字列は除外してください。

### CEFRレベルの定義
- **A1（入門）**: 日常的な表現や基本的なフレーズを理解し、使用できる。
- **A2（初級）**: ルーチンな状況で必要な表現を理解し、簡単なコミュニケーションが取れる。
- **B1（中級）**: 仕事や学業、旅行などで必要なコミュニケーションを自信を持って行える。
- **B2（中上級）**: 複雑な議論や専門的な内容を理解し、効果的にコミュニケーションできる。
- **C1（上級）**: 幅広いテーマについて深く理解し、流暢かつ自発的に表現できる。
- **C2（熟達）**: 複雑で高度な内容を母語話者と同等に理解し、表現できる。

### 出力形式
以下の形式で出力してください（各単語とそのレベルをカンマで区切り、1行に1つのペア）:

word1,A1
word2,B2
...

### 単語リスト:
{words}
"""
```

あくまでGeminiの判断なので、厳密なCEFRレベルとは異なるのかもしれませんが、これによって50000語程度あったリストは、CEFR基準の難易度付きで37000語程度まで絞られました。

### Geminiに語義・例文・例文和訳を生成させる
同じ要領で、100行ごとにGemini APIを用いて、語義・例文・例文和訳を生成させました。
以下のようなプロンプトを用いました。

```py
PROMPT_TEMPLATE = """
次の単語リストに対して、単語の日本語訳、英語例文、およびその英語例文の日本語訳を生成してください。
出力は、単語、単語の日本語訳、英語例文、英語例文の日本語訳の順番に、**カンマ区切り**のCSV形式で記述してください。
各フィールド（列）は**必ずダブルクォートで囲んでください**。例えば次のように出力してください:

"word","Japanese translation","English example sentence","Japanese translation of example sentence"

### 単語リスト:
{words}
"""
```
これで最初にお見せしたような37000語レベルの語義・例文・例文和訳つきの英単語リストが得られました。

しかし、まだ少しだけ固有名詞が含まれていたので、CSVファイル内を「人名」「地名」などで検索し、手動で一部削除しました。

![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/d45bf992-e3ff-8dd4-ab2c-81e40884c512.png)

## 例文を読ませよう！（Style-Bert-Vits2）
さて、英単語、難易度ランク、語義、例文、例文和訳つきの37000語のリストが得られました。CSVなのでちょっとチャンク分けしてあげればAnkiやQuizletにインポートするのも用意だと思います。

でも…。

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/7fcbc55f-20c9-ad3b-036c-53faf7c57377.png)

なら…。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/8dfcb301-0115-1c45-c622-88e22474cf70.png)

ということで例文の音声をTTS技術を用いて作ることにしました。
その中でも自然な音声を合成できるStyle-Bert-Vits2を用いました。

使ったのは以下の「凛音エル」という自作キャラのモデルです。
英語版のモデルと日本語版のモデルがあります。

https://huggingface.co/kokuren/RinneElu

余談ですが、英語版モデルはLJspeechというアメリカ英語の女性話者音声コーパスから作成したモデルと、凛音エルの音声データセットから作成したモデルをマージして作成しました。

### Style-Bert-Vits2ライブラリを利用する

https://github.com/litagin02/Style-Bert-VITS2/blob/master/library.ipynb

今回はモデルが既にあるので、上記を参考にして推論部分のみのライブラリを利用して音声を生成しました。英語のモデルでは英語用のbertを、日本語のモデル（JP-extra）では日本語用のbertをそれぞれ使いましょう。

```py
# 英語用のbertモデル
bert_models.load_model(Languages.EN, "microsoft/deberta-v3-large")
bert_models.load_tokenizer(Languages.EN, "microsoft/deberta-v3-large")

# 日本語用のbertモデル
bert_models.load_model(Languages.JP, "ku-nlp/deberta-v2-large-japanese-char-wwm")
bert_models.load_tokenizer(Languages.JP, "ku-nlp/deberta-v2-large-japanese-char-wwm")
```

これで例文音声の生成ができました！

## GitHubで公開

https://github.com/kokuren333/OANC_37k

上記githubにて作成したリストと音声ファイル群を公開しました。
ライセンスはODC-PDDLというほとんどパブリックドメインと同じもので公開しているので、表示不要、改変可、営利可、継承不要です。

余談ですが、ライセンスに関しては以下記事を参考にしました。

https://zenn.dev/sanami/articles/37d0028142b0a6

正直このままの生データではまだ使いにくさなどがあると思うので、どうぞ自由に改変をしたりアプリに組み込んだりなどしていただけると嬉しいです。

### 課題
まず、Geminiで語義を生成しているため、訳が完全に正しいとは言い切れない部分があります。

Web上の英和辞典などから語義をスクレイピングしてくる案も考えたのですが、オープンに公開するのが難しいかもしれないと考え、今回は語義ごとGeminiで生成してしまいました。
（ただし、例文やその訳と同時に出力させていることからある程度は信頼できると考えています。）

また、CSVデータや音声データがそのままでは学習用途としては扱いにくいので、何らかの学習インターフェースが必要だと感じました。

AnkiやQuizletに取り込むか、音声データを繋いで長めの動画や音声とするか、このデータをもとに英単語学習用の簡易アプリを開発するか、などの工夫が欲しいところです。

## おわりに
ここまでお付き合いいただきありがとうございました！
以下の精神を大事にしたいですね。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/bac81cb4-01bc-0e80-76e3-39c320c73fa0.png)