2021年11月15日月曜日

学年別の熟語辞書を作った

学年別の熟語辞書 を作りました。 というか形態素 N-gram からの良い熟語生成を思い付いてしまったので、 以前作った ngram-idioms を一から作り直しました。



以前の生成方法

ngram-idioms では、 形態素 Ngram を前後のつながりを見ながら形態素を複数つなげて熟語らしいものを作り、 テンプレートマッチングでノイズを除去していました。 しかしこの方法は面倒だし、形態素 N-gram の gram 数の影響を受けて、 頻出熟語しか抽出できない問題があります。 今までのような教育用途だと問題ない精度で抽出できるのですが、手作業のノイズ除去が必要だし、 数は少なくなるし賢い生成方法ではないです。

改良案

どこまで真面目に作るかの問題があるのですが、シンプルに作るなら以下の方法で良さそうです。 手作業のメンテが不要なのが良い。
  1. 日本語ウェブコーパス 2010 の 1〜7gram から熟語っぽいものを生成
  2. SudachiDict で熟語判定
  3. SudachiDictinappropriate-words-ja で地名と人名と禁止語を削除
  4. 品詞判定で不要語を削除
  5. 漢字レベルに合わせて形態素を分類

ポイントは形態素解析辞書の生起コストに違和感があるので、形態素 N-gram のカウントを使うところ。 形態素解析辞書は Neologd でも良いですのが、より信用できる SudachiDict にしました。 以下の記事は熟語生成にもとても参考になります。 なんだ今なら簡単に作れるじゃないかーと思いました。 まあ以前作ったときは SudachiDict の固有名詞フィルターがどれくらい使えるかわからなかったので仕方ない。 やっぱ色々素振りして遊んでないと駄目ですね。

難しいところ=熟語 or 複合名詞

熟語かどうかの判定はとても難しいです。 そのよくわからないところを形態素の 2〜7gram を見て語彙生成した後、SudachiDict にぶん投げて解決しています。 7gram にもなると早口言葉みたいなのがたくさん出てきます。

SudachiDict なら A〜B単位がまさに熟語となります。C単位は微妙なところ。 C単位は「正多角形」のような熟語がほんの少しある一方、「八戸市内」のような熟語か?というものが大量に混ざってきます。 熟語かどうかはすごく判断が難しくて、考え始めるとキリがないです。 そもそも「歴史」も「草木」も熟語じゃなくて複合名詞では?と言われれば「確かにそうかも」となるように、本質的に難しいです。 私は雑なので頑張らず、SudachiDict に丸投げし、B単位までにしました。

SudachiDict は本当によくできた辞書なので、全体的にいい感じに生成されているように見えます。 データを見てもわかるのは、三字熟語まではまあまあ人間的にもわかりやすいですが、 四字熟語以上は熟語なのかしっかり考えないとわからないもの (C単位) のほうが多くなってきます。 ゲームで使えるのは三字熟語まででしょうね。 また基本語彙を押さえておけば三字以上の熟語の大半の意味はわかることも確認できました。

ちなみに昔の実装も面白いので ngram-idioms は残してあります。 ngram-idioms では手動のフィルターを結構作ったので、差分の比較に便利です。 たとえば SudachiDictでは「花王」「山門」「太一」「東横」などを普通名詞にしていますね。 この中では「花王」は固有名詞が正しい気がします。 ただ企業名のフィルターはかなり難しいので、まあ無視で良いかな…。

このへんを頑張ろうとするなら、Mecab をフィルターにすれば、頻度を考慮するので名前として除外されます。 最初は Mecab を併用して頑張ってノイズ除去していましたが、 ビルドに時間が掛かりますし、SudachiDict の進化に期待して最近は特に除去していません。

かなり良いものができたので、以前作ったアプリで関係のあるものは全部更新しておきました。 まずは統計の歪みが完璧に消えて完成度が高くなったと思います。 また難易度調整もしやすくなり、ドリル系のアプリもかなり使いやすくなりました。 なるべく基本語彙から学んでいくことで学習効果も高くなったはず。

学年別の基本語彙一覧を作った

なんとなく知りたかったので、学年別の基本語彙一覧を作りました。 ようするに graded-enja-corpusmGSL の日本語版です。



作り方

どこまで真面目に作るかの問題があるのですが、シンプルに作るなら以下の方法で良さそうです。 手作業のメンテが不要なのが良い。
  1. 日本語ウェブコーパス 2010 の 1gram を正規化して語彙に変換
  2. 機械的に多少ノイズ除去
  3. SudachiDictinappropriate-words-ja で地名と人名と禁止語を削除
  4. 品詞判定で不要語を削除
  5. 表層形から原形に直した上で頻度順にソート
  6. 漢字レベルに合わせて形態素を分類

地名や人名のフィルターに SudachiDict を使ってますが、Neologd でも良いかも知れません。 もっとも Neologd は 2gram 以上でないと意味がないので、SudachiDictで 良いんじゃないかな。 処理自体は 2gram にも対応できますが、してません。 2gram を処理すると複合語をさらに抽出できる可能性がありますが、作りたかったのは「語彙一覧」ではなく「基本語彙一覧」です。 別にいらないかなと。 技術的には IPADic では UniDic を使ったほうが厳密ではないか、など色々細かい話はあるけど、まあ無視できるレベルでしょう。 IPADic の粒度のほうが実用上は便利です。

語彙と基本語彙

「語彙」という言葉には曖昧性があり、慣用句を含むこともありますが、 「基本語彙」と書いた時には形態素の原形を指している気がします。 「基本語彙」がわかっていれば「語彙」も、慣用句などの特殊ケースを除いて意味がわかるはずです。 今回作った一覧表は、先生が学年ごとにどれくらい「基本語彙」を覚えるべきか確認したり、 生徒が学年別にサラッと眺めて「基本語彙」を覚えてもらうために作りました。

扱いの難しいもの=接頭辞、接尾辞

この辞書には、固有名詞や複合名詞の他にも、単体では意味の通じない接頭辞や接尾辞は含まれません。 具体例を挙げると「一ヶ月」は複合語、「ヶ月」が接尾辞なので含まれません。 そこをどこまで真面目に含めるかという話はあるのですが、 (1) 接頭辞や接尾時は明らかに実用上ノイズのほうが多く、(2) 後からデータを活用しにくくなるので、削除することにしました。 接尾時や接頭辞は基本語彙じゃないの?と言われると、ぐぬぬ…ですが、別に辞書を作ったほうが平和と思ったので、勘弁。

接頭辞や接尾辞も考慮した辞書を作るには、2gram で複合名詞化すればいいです。 ただ基本語彙ではないように思うので、今回はやってません。 ひらがなはどこまでやっても微妙な出力ですが、こればかりは仕方なさそう。

2021年11月14日日曜日

漢字判定のいろいろな正規表現

漢字判定の正規表現は奥が深いです。 [一-龠] の記述がネット上にたくさんあるのですが、 「々ヵヶ」といった頻出語をサポートしてないことに気付きました。 みんな苦労しているみたい。 たまに必要になるので、自分なりの考えをメモしておきます。

結論はこちら。
[\u4E00-\u9FFF]        # CJK 統合漢字の基本多言語面
[\u4E00-\u9FFF𠮟]      # 常用漢字 (これもありかも)
[\u4E00-\u9FFF々]      # 実用上ここまではサポートしたい
[\u4E00-\u9FFF々ヵヶ]  # +頻出語
[\u4E00-\u9FFF々ヵヶヽヾゝゞ〃仝]  # +くり返し記号など
/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}]/u    # Unicode 15.1 漢字すべて
/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}々]/u  # Unicode 15.1 漢字すべて

漢字の指定

漢字の指定方法は、[一-龠] というものがネット上で多く見受けられ、私も利用していました。 最後の漢字が「龠」つまり U+9FA0 である理由は正直よくわからないのですが、たぶん Unicode 1.1 のときの指定を使っているのでしょう。 「龍」というよく使われる単語が、U+9F8D にあるので、それ以上の範囲削減は不可能です。 現在はさらに拡張されていますし、CJK 統合漢字の基本多言語面に基づく、[\u4E00-\u9FFF] が最も厳密な指定だろうと思います。

ちなみに [\u4E00-\u9FFF] の指定は JIS 第 2 水準までを想定した指定ですが、新常用漢字の「𠮟」を処理できないなどの問題があります。 ではどうするかというと、Unicode プロパティの /\p{scx=Han}/u を使う方法もあります。 ただこれでも「々」は判定できませんし、記号が含まれる致命的な問題があります。 同じことは /\p{scx=Hiragana}/u などにも言えて、Unicodeプロパティは意外と扱いに困ります。

ちょっと前に /[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{2FFFF}]/u という漢字判定もよく見かけました。 これもいい感じではあるのですが、今は CJK統合漢字拡張 H 以降に対応できていません。 そのため第3漢字面もサポートしないといけませんが、U+38000〜U+3AB9FF は甲骨文字がアサインされる予定があります。 甲骨文字も漢字と言えば漢字なのですが…漢字としてサポートするべきかは迷うところです。ひとまずサポートしないほうが良さそうかな。 そのギリギリ手前の U+37FFF までにするか、CJK統合漢字拡張 H の最終文字 U+323AF までにするかは難しいところです。 結局どこまでにするのが良いかは Unicode と CJK統合漢字の進化をよく見ながら決定するしかないのだろうと思います。

Unicode 15.1 (最新) の漢字すべてを想定した時に、現状でたぶん一番楽そうなのは、/[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}]/u という漢字判定です。 Unicode フラグ u を付けた上で、\u{} で指定するのがポイント。

参考:

漢字に関連する語の指定

/\p{scx=Han}/u は使えそうで使い物にならないことが多いので、漢字や用例、文字コードに対する知識を深めるのは割と重要そうです。 くり返しを意味する「々」は漢字ではないらしいのですが、日常生活でも頻繁に出現します。 そのためたいていはサポートしないと、色々な不都合が生じます。

また「一ヶ所」のような使い方をする「ヵヶ」もたいてい必須語になります。 ただ「ヵヶ」は助数詞可能な名詞として出現し、必ず複合語として使われる漢字なので、サポートしなくていい場合もありそうです。 「ヵヶ」は Unicocde 的にはカタカナの一部として扱われているので、カタカナ語との重複に注意が必要です。 SudachiDict を見ると「ヶラヶラ」「スヵッ」「ヵッォ」などの謎エントリ以外はカタカナとして使っている用例が見当たりません。 単語としての単位ではカタカナからも除外したほうがよく、プログラミング的には漢字として扱って良いのではないかと思います。 余談ながら、カタカナの正規表現では、「ヵヶ」を除外しないでもいい時は [ァ-ヶ] と指定し、除外したい時は [ァ-ヴ] と指定するのが良さそうです。 /\p{scx=Hiragana}/u や /\p{scx=Katakana}/u はこのへんも扱いにくく、非常に使いにくい指定だったりします。 一番致命的なのは「ー」がひらがな/カタカナ判定されたり、記号が含まれるところですけどね。
くり返し記号は他にも「ヽヾゝゞ〃仝々」というものがあります。 先に述べた「々」もこの一部です。 現代語ではほぼ出現しませんが、「いすゞ」のような特殊例があります。 とはいえこちらは基本的には無視して大丈夫でしょう。 他にも「〆〇〻」といった漢字も、稀に出現はします。 ただ指定も面倒臭いので、これらは無視しても大丈夫でしょう。 ほとんどのケースでは、[\u4E00-\u9FFF々]、真面目にやるなら /[\u3400-\u9FFF\uF900-\uFAFF\u{20000}-\u{37FFF}々]/u で大丈夫じゃないでしょうか。
利用用途に応じて範囲を調整しないといけないのが地味に面倒です。

2021年11月2日火曜日

ASMRフォニックスとASMR英単語を作った

英語の聞き流しはリスニング力や定形表現、 アクセント、英単語に効果があると言われているようです。 記憶力が高い子供のほうが良いらしいけど。 たぶん音素の少なさが影響しているんじゃないかな。 英単語を覚える場合なら反復暗記に使えるのが大事と思うので、 聞き流しでチェックできるアプリとして、ASMRフォニックスASMR英単語 を作ってみました。



聞き流しと言っても、完全に聞き流されるとさすがに意味がなさそうなので、 多少の集中力を求める仕組みを用意しました。

最初のうちはスペル付きで発音した後に単語の発音するようにしてみました。 フォニックスを学ぶときに英語の歌を活用している姿をよく見ますが、 それをもっとシンプルにしました。 慣れてきたら同じ英単語を3回しゃべるので、対応する英語を思い浮かべてもらい、日本語で確認します。

ただ聞いているだけだと続かないけど、 ほんの少しでも頭で整理して思い浮かべさせるステップを入れることで、 ダラダラやっていても効果的に学べるようにしたつもりです。 基本は聴くだけですが、もう一度聞く機能などを付けておきました。

完全な聞き流しには案外ならないので、意外と良いかも知れません。