はじめに
こちらは後編の記事になりますので、まずは前編をご覧下さい。
↓ 今回作成した単語データを使用した学習の様子(記事の最後にダウンロードリンクがあります!)
実行結果だけ見たい!という方はこちら→実行結果
目次
- 前回の振り返り
- 改善方法について
- プログラム内で使用する単語が多い
- case insensitiveになっていない
- stackoverflowのページにオーバフィットしている
- コードの全体像
- 改善点と追加機能
- プログラム内で使用する単語が多い
- case insensitiveになっていない
- stackoverflowのページにオーバフィットしている
- 単語の翻訳
- その他
- 実行結果
- 結論
- 【重要】英単語帳を作るには
- データのダウンロード
前回の振り返り
仮説と検証結果
前回は前編ということで、プログラムで自動生成してしまえば
もう英単語帳購入の必要はないんじゃないか?
という仮説の簡単な検証を行なっていきました。 前回の実行結果の一部がこちら。rank:0 word: ('i', 96)
rank:1 word: ('python', 86)
rank:2 word: ('ago', 50)
rank:3 word: ('bronze', 36)
rank:4 word: ('have', 29)
rank:5 word: ('×', 25)
問題点
前回の検証結果として下記3つの問題点があることが見えてきました。
- プログラムのみで使用する単語が存在する
- case insensitiveになっていない
- stackoverflowのページにオーバフィットしている
今回はこれらの課題を解決し、
エンジニアにとって必要な英単語を最効率で学習
できる英単語帳の作成を目指していきます。改善方法について
プログラム内で使用する単語が多い
def、jやkなどの単語のことになるのですが、プログラム的に除去していく方法としては以下のようなものが挙げられます。
- プログラムのみで使用するような単語のリストデータを作成し、フィルタリング
- 自然言語として意味のなさないものを除去する+2文字以下は単語とみなさない※1
- 品詞の解読精度を向上させる
1と3は時間がかかるので、TextBlobの機能が生かせる2を採用していきます。
まずは、こちらをご覧下さい。
>>from textblob import Word
>>print(Word("list").definitions)
>>['a database containing an ordered array of items (names or topics)',...]
こちらで分かる通り、単語に意味が存在する場合definitions
というアトリビュートを呼ぶと、定義をリストで返してくれます。
同様に'def'の定義を見てみると
>>from textblob import Word
>>print(Word("def").definitions)
>>[]
のように、空のリストが返されますので、こちらを利用して本来の英語には存在しない単語を除去していきます。
case insensitiveになっていない
これは下記のようにstringのメソッドlowerを呼んで、全て小文字にすることで対応していきます。
'string'.lower()
stackoverflowのページにオーバフィットしている
ここが良い単語帳を作成するにのに最も重要なポイントとなりそうです。「エンジニア」をターゲットとすると少し幅が広くてデータをとるのが大変なので、下記2点を行っている人にフォーカスした単語帳を作成することとして5つのwebページをピックアップしてみました。
- Pythonを使用している
- AIを使用したバックエンド開発を行っている
1/5 AWS Lamda
https://aws.amazon.com/lambda/
2/5 Python3.8 What's new
https://docs.python.org/3/whatsnew/3.8.html
3/5 Docker what is container
https://www.docker.com/resources/what-container
4/5 Wikipedia Artificial intelligence
https://en.wikipedia.org/wiki/Artificial_intelligence
コードの全体像
基本的には前回のコードを使いまわしていますが、今回は複数のサイトにアクセスするためのfor文が必要となったので、一部関数の切り出しと修正を行っています。
from enum import Enum, unique
from typing import List, Tuple, Dict
import requests
from bs4 import BeautifulSoup as bs
from textblob import TextBlob, Word
from textblob.exceptions import NotTranslated
URLS = (
('stackoverflow', 'https://stackoverflow.com/questions/tagged/python'),
('AWS', 'https://aws.amazon.com/lambda/'),
('wikipedia AI', 'https://en.wikipedia.org/wiki/Artificial_intelligence'),
('Docker', 'https://www.docker.com/resources/what-container'),
('Python3.8 doc', 'https://docs.python.org/3/whatsnew/3.8.html'),
)
PARSER = "html.parser"
UNACCEPTABLE_LENGTH_OF_WORD = 2
@unique
class PartOfSpeechToLearn(Enum):
JJ = 'Adjective'
VB = 'Verb'
NN = 'Noun'
RB = 'Adverb'
def single_page_text_from_url(url: str) -> str:
res = requests.get(url)
raw_html = bs(res.text, PARSER)
texts_without_html: str = raw_html.text
return texts_without_html
def filter_for_wordbook(morph: TextBlob) -> List[str]:
word_and_tag: List[Tuple[str, str]] = morph.tags
part_of_speech_to_learn = tuple(pos.name for pos in PartOfSpeechToLearn)
filtered_words = []
for wt in word_and_tag:
word: str = wt[0].lower()
part_of_speech: int = wt[1]
if part_of_speech not in part_of_speech_to_learn:
continue
if word in filtered_words:
continue
if (
len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or
len(Word(word).definitions) == 0
):
continue
filtered_words.append(word)
return filtered_words
def show_top_50_common_and_uncommon_words(word_and_count: Dict[str, int]) -> None:
words_by_descending_order: List[Tuple[str, int]] = sorted(
word_and_count.items(),
key=lambda x: x[1],
reverse=True
)
print('the most common words')
for i, word_and_count in enumerate(words_by_descending_order[:10]):
word: str = word_and_count[0]
count: int = word_and_count[1]
try:
meaning_jp = str(TextBlob(word).translate(to='ja'))
except NotTranslated:
meaning_jp = word
print(f'rank:{i + 1} word: {word} count: {count} meanings: {meaning_jp}')
print('the most uncommon words')
for j, word_and_count in enumerate(words_by_descending_order[:-11:-1]):
word: str = word_and_count[0]
count: int = word_and_count[1]
try:
meaning_jp = str(TextBlob(word).translate(to='ja'))
except NotTranslated:
meaning_jp = word
print(f'rank:{j + 1} word: {word} count: {count} meanings: {meaning_jp}')
if __name__ == '__main__':
word_and_count: Dict[str, int] = {}
for url in URLS:
print(f'website: {url[0]} url: {url[1]}')
texts_without_html = single_page_text_from_url(url[1])
morph = TextBlob(texts_without_html)
filtered_words = filter_for_wordbook(morph)
for word in filtered_words:
count = morph.words.count(word, case_sensitive=False)
if count == 0:
continue
if word not in word_and_count.keys():
word_and_count[word] = count
else:
word_and_count[word] += count
print(f'length of word_and_count: {len(word_and_count)}')
show_top_50_common_and_uncommon_words(word_and_count)
それでは、前回から改良した点をみていきましょう!
改善点と追加機能
プログラム内で使用する単語が多い
前回内包表記でフィルタリングしていた部分を全て関数に切り出してif
でフィルターするようにしています。シンプルな処理であれば内包表記がnamespaceの節約と処理効率の面で便利ですが、複数の条件でデータを処理する場合にはif
文を使用する方がシンプルでベターかと思います。
if
に関してはネストを避けるために、処理が軽そうなものから順にcontinue
していくようにしています。
def filter_for_wordbook(morph: TextBlob) -> List[str]:
word_and_tag: List[Tuple[str, str]] = morph.tags
part_of_speech_to_learn = tuple(pos.name for pos in PartOfSpeechToLearn)
filtered_words = []
for wt in word_and_tag:
word = wt[0].lower()
part_of_speech = wt[1]
if part_of_speech not in part_of_speech_to_learn:
continue
if word in filtered_words:
continue
if (
len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or
len(Word(word).definitions) == 0
):
continue
filtered_words.append(word)
return filtered_words
上記コードのここで、プログラムでしか使用しない単語をフィルタリングしています。
if (
len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or
len(Word(word).definitions) == 0
):
continue
case insensitiveになっていない
先ほどのコードの下記部分で、大文字小文字が違うだけで同じ単語を同一データとして重複を避ける処理を入れています。
for wt in word_and_tag:
word = wt[0].lower()
if word in filtered_words:
continue
stackoverflowのページにオーバフィットしている
先ほど紹介したwebサイトをtuple
型でURLS
として定義しています。
URLS = (
('stackoverflow', 'https://stackoverflow.com/questions/tagged/python'),
('AWS', 'https://aws.amazon.com/lambda/'),
('wikipedia AI', 'https://en.wikipedia.org/wiki/Artificial_intelligence'),
('Docker', 'https://www.docker.com/resources/what-container'),
('Python3.8 doc', 'https://docs.python.org/3/whatsnew/3.8.html'),
)
単語の翻訳
中身はGoogle翻訳ですが、単語の翻訳もTextBlobライブラリを使用しています。
NotTranslated
は、python
という単語のように、翻訳してもpython
となるようなものを翻訳しようとした時にraise
されるものになります。
try:
meaning_jp = str(TextBlob(word).translate(to='ja'))
except NotTranslated:
meaning_jp = word
その他
お気づきになった方もいらっしゃるかもしれませんが、下記のコードで
for word in filtered_words:
count = morph.words.count(word)
if count == 0:
continue
if word not in word_and_count.keys():
word_and_count[word] = count
else:
word_and_count[word] += count
このように、カウントがゼロの場合、次のループへ移行する文があります。
count = morph.words.count(word)
if count == 0:
continue
具体的にはe.g.,
のような文字列を解析した際、トークナイズ時はe.g.
、品詞毎に分解した際はe.g
となるため、e.g.,
のような単語がきた時にcount==0
となるのでこちらのコードを挿入しています。
自然言語処理とTextBlobの使い方に関してまだまだ勉強不足ですので、ある程度知見がたまったら別途TextBlobの使い方まとめ、みたいな形で記事を出した際に解説していきたいと思います。
実行結果
前回の問題点は無事改良されているのでしょうか................?
実行結果はこちら..................................!
the most common words
rank:1 word: intelligence count: 327 meanings: 知性
rank:2 word: artificial count: 280 meanings: 人工的な
rank:3 word: help count: 190 meanings: 助けて
rank:4 word: python count: 186 meanings: python
rank:5 word: error count: 184 meanings: エラー
rank:6 word: target count: 168 meanings: 目標
rank:7 word: new count: 134 meanings: 新着
rank:8 word: learning count: 129 meanings: 学習する
rank:9 word: machine count: 125 meanings: 機械
rank:10 word: have count: 120 meanings: 持ってる
the most uncommon words
rank:1 word: donate count: 1 meanings: 寄付
rank:2 word: math count: 1 meanings: 数学
rank:3 word: inspect count: 1 meanings: 調べる
rank:4 word: ignore count: 1 meanings: 無視する
rank:5 word: processor count: 1 meanings: プロセッサー
rank:6 word: queue count: 1 meanings: キュー
rank:7 word: depend count: 1 meanings: 依存する
rank:8 word: pretend count: 1 meanings: ふり
rank:9 word: decrease count: 1 meanings: 減少
rank:10 word: allocator count: 1 meanings: アロケータ
おおおおおおおおっ!
結論
まずはもう一度仮説に振り返ってみたいと思います。今回のシリーズでの仮説は
Pythonで自動的に作成すれば、
もう英単語帳購入の必要はないんじゃないか?
というものでした。 こちらの仮説に対しての結論としては、英単語帳は購入しなくても、プログラムで作れる!
といえそうです!!!※2【重要】市販のような英単語帳を作るには
実行結果をみていただければわかりますが、今回抽出できた単語だけでもかなり有用なことが分かります。ただ、**市販の単語帳**と同等のものを作るにはインターフェースと3つの追加情報が必要になってきます。
インターフェース
市販の単語帳には、本、もしくはアプリといった問題形式で覚えたり、内容の確認をしやすくするためのインターフェースを備えています。
今回のように自作英単語データを学習に使用したいという方には、私のブログでも紹介していますAnkiアプリが超おすすめです!!素のAtom使ってる人がIDEを使いこなした時ぐらい感動します!
先日Twitterでたまたま見かけたのですが、連続起業家で有名な家入一真さんもAnkiを使用しているようです。
会食が無い分、毎晩ずっとNGSL3000語、NAWL1000語、英単語を暗記しまくってる。AnkiとQuizlet便利すぎ
— 家入 一真@クラファンのCAMPFIRE (@hbkr) April 9, 2020
PC版は無料でインストールできるので、PCだけであれば完全無料で英単語学習ができそうです。※3
【英語学習に必須】Ankiアプリとは?
3つの情報
市販の英単語帳には通常、下記の3つの情報がほぼ確実に含まれています。
- 単語の翻訳詳細(品詞、他の意味)
- 例文
- 例文の翻訳
1.単語の翻訳詳細(品詞、他の意味)や2.例文に関してはTextBlobの機能を使えばうまく引っ張ってくることができるのですが、例文の翻訳が問題となってきます。
こちらの記事でも紹介しましたが、Google翻訳を使用して文章を翻訳するとコンテクストの問題やそもそもの翻訳が違うなどの問題がでてくるので、正しい学習ができない可能性があります。
まだGoogle翻訳で消耗してるの?
しかし
人力であれば、例文の**自然な翻訳**をしていくことはまだ可能です。もし、私のTwitterのフォロワーが1000を超えたら、今回作成したプログラムをさらに改良し例文も含めた、
エンジニア向けの最強英単語帳
のデータを配布していきたいと思います。注釈
※1 下記のサイトを見た感じだと無視して良さそうです
https://en.wikibooks.org/wiki/Scrabble/Two_Letter_Words
https://en.wiktionary.org/wiki/Category:English_one-letter_words
※2 英単語帳を英単語とその翻訳のデータセットとして定義すれば
※3 Ankiさん、広告費貰えるわけでもないのに激推ししておきましたよ!