2022年12月27日火曜日

JavaScript の Grid ライブラリ 2022 雑感

久々に JavaScript の Grid ライブラリを利用したので、ライブラリの選定をしました。 以前書いた記事 から 5年も経っていることになりますが、 テーブルでたくさんのデータを表示する機会は、最近ありませんでした。 1万件くらいのデータでないと適用しにくいので、利用機会も減った気がします。 少し前に投稿した autocomplete も 1万件くらいで限界が来る ので、そのへんが JavaScript で扱いやすいデータの限界かも知れません。

5年も経てばどのライブラリも成長し、機能面ではどれもそれほど遜色がありません。 Grid ライブラリは、Pagination できれば理屈的には速度的に大差なくなるはずなので、 あとはどれだけ使いやすいかという話になります。 以前見つけたライブラリの他にも、GitHub で探せば色々と見つかります。 有名どころでは以下のライブラリが追加されていましたが、 以前ほど詳細に比較する気は起きなかったので、良さそうなものを軽く試して結論を出しました。

結論

結論だけ言えば、今から Grid ライブラリを使うなら、 Bootstrap TableDataTablesGrid.js のどれかが良いと思いました。 Bootstrap Table は名前から考えると Bootstrap 依存に見えますが、そういう訳ではなく、様々な CSS Framework から利用しやすいライブラリです。 jQuery 以外の依存はほとんどないライブラリです。Grid.js はさらに依存が小さいライブラリです。 DataTables は Bootstrap Table とよく似た設計で jQuery 依存ですが、見た目がかっこいいです。 どれも他のライブラリと比較して拡張性が高く、シンプルなのが良いところです。

最近の人気ライブラリの特徴と欠点

ちなみに以前まとめた Grid ライブラリは特定の JavaScript Framework に依存するものが多いです。 しかし流行り廃りが激しく、採用の決め手になりません。 そのような理由もあってか、最近では Vanilla JS もしくは主要 JavaScript Framework すべてに対応するライブラリが増えたように思います。 そのせいでレンダリング処理に自前感が出てくることが増えますが、ここが次のポイントだと思っています。

いくつかのライブラリでは、table 要素を使わずに複雑なレンダリングをしているものがあります。 こうしたライブラリは設定が複雑過ぎてユーザ側が使いこなせません (少なくとも私は無理)。 昔のライブラリも同じような欠点はあるのですが、CSS Framework と CSS Variables が普及してきたので、 見た目を整えないと違和感が生じることが増えています。 結果として、見た目を整えられるくらいには設計がシンプルなライブラリが良いと私は思いました。

ユーザが拡張することを考えると、JavaScript Framework より CSS Framework に依存したほうが良く、 設計がシンプルであるものが良いです。このような設計を意識したライブラリを絞り込むと、 今のところ良さそうなのは Bootstrap TableDataTablesGrid.js の 3つでした。

JavaScript Framework の依存性や、ライブラリの特性を見ていて思った個人的な印象を一言でまとめると、ライブラリに固有の概念を持ち込み過ぎないほうがいい です。 固有の概念を持ち込めば持ち込むほど、いざ廃れた時の対応が困難になります。 ユーザが扱いきれないほど複雑なレンダリングをするフレームワーク / ライブラリともなると、すぐ対応不可能になることが予想できます。 その点 jQuery は人畜無害で、一度やる気を出せば Vanilla JS へすぐ変換できます。

Free MIDI では Bootstrap Table を採用

ちなみに今回作ったアプリ Free MIDI では、 (1) 列ごとの検索がほしかったのと、(2) 列にボタンを付けたい要望があったので、 それらを実現可能な Bootstrap Table を採用することにしました。 1 に関しては自前の実装でもなんとかなることが多いですが、2 は意外と実装しにくかったりします。 Grid.js だとデフォルトで上記の機能をサポートしておらず、 DataTables より Bootstrap Table のほうが実装が楽で、より高機能だったのが決め手になりました。 Bootstrap Table はたぶん機能性を求めると最も使いやすいライブラリの気がします。 jQuery の資産の大きさと、簡潔さを改めて感じました。

2022年12月16日金曜日

MIDI を Waterfall + ピアノで楽しむアプリを作った

MIDI を Waterfall + ピアノで楽しむアプリ Waterfall Piano を作りました。 中国はこの形式でピアノを学ぶことが一般的になったらしいので、 似たようなことをしてみたいと思って作りました。音楽教室などでも使えるかも知れません。



実装上は UI の苦労が多かったです。ピアノロールの綺麗な表示自体、かなり苦労しました。 最初はピアノロールを可変表示にしたほうがゲーム性があって良いかと思っていたのですが、 長さが短くなり過ぎると UI が崩れて使いにくくなると気付きました。 処理をいじって綺麗な表示にしようとするのは、かなり大変そうです。 とはいえ 88鍵盤すべて表示するとタブレットでは遊べなくなってしまいます。 人間の世界は基本的に可変長 UI では動いていないので、 ゲーム化するにはある程度の固定長 UI にしないと、成立させにくいです。

どうするのがいいか悩んだ挙句、可変長表示に対応しつつ、現状ではゲームではなく楽譜としての利用をメインにすることにしました。 今は Waterfall 型の楽譜をタブレットで表示しながら、本物のピアノか MIDI キーボードで遊ぶことを想定しています。 機能的には途中から再生したり、速度を変えて練習する機能があると嬉しいと思ったので加えています。 音ゲー的な要素はほとんどありませんが、 本物のピアノか MIDI キーボードで遊べれば十分とも思ったので、ひとまず良しとしています。

とはいえ将来的には MIDI キーボードの入力にも興味はあります。 ピアノロールの長さを制限したピアノのアプリなども興味はあります。 特に最近 Firefox が Web MIDI API に対応したので、MIDI キーボードはだいぶ希望が見えてきたように思います。 ただ現状では、どちらも検討材料が多いのが現状です。 MIDI 系のアプリは手探りのところが大きいので、 簡単にできることを一歩ずつ固めながら作っていきます。

再生自体は ほぼすべての MIDI ができると思います。 音質に関しては、クラシックなら完璧です。 とはいっても真面目に聞くと若干ノイズはある。 ゲーム音楽は音量調整か残響音あたりに少し違和感があるかも。 まあライブラリの限界ということで現状は仕方ないです。 それでも個人的には、何の準備もなしに古の名作ゲームの MIDI をピアノと一緒に眺められるようになっただけで、割と満足はしています。 Nepheshel はいいぞ。

2022年12月3日土曜日

人類 vs 生成モデル

たまには駄文。画像やイラストの生成モデルが流行してから 半年くらい経ったのかな。 最近は対話 AI も出てきて凄いことになってきました。 そろそろ答え合わせする時が来ているのかなと思うので、感じているところをまとめてみます。

法律が穴だらけ (補足: 1/3)

現状の法律的には、違法データ (Danbooru) から学習しても、なんと違法にはならない。 あくまで違法データの提供元 (Danbooru) が悪いとなる。 ただ Danbooru もおそらくは脱法する気はそこまでなく、管理しきれない違法データがある状態と理解している。 まさかそのようなサイトから勝手に学習して商用化する行為 (WD, Novel AI) は、さすがに意図していないと思われる。 この時点で権利侵害だし、違法データの中に NC があったとしたら、本来の意図に反しているし権利も侵害しているから、 商用はライセンス違反…と思ってたけど違うのかな。 それより問題なのは、違法的に学習されたモデル (NAI) をネットに放流して、再学習したりしている界隈全体。 放流されたモデルから再学習した場合は、 データから学習している訳ではないので、NAI に責任を押し付けることで、なんと違法にならない。 こうして俗に言うライセンスのたらい回しがいくらでもできてしまっているのは、いかがなものか。

個人的な感覚では AI イラストの一定量は著作権侵害

AI イラストの生成モデルの多くは、収集の時点で著作権やライセンスを意図的に無視しているので、サービス化はアウトと個人的には思う。Copilot なども同じ。 ちなみに技術そのものはフェアユースと思われるし、良いものだと思うし、止まらないと思う。 Clean Diffusion のような路線が、長い目で見れば一番良さそう。 テキスト版の Common Crawl を画像で作っても、中身のシャッフルができる訳でもなく、それは本当に OK なのかな。

イラスト生成とゲーム実況はよく似ている

ゲーム実況も元コンテンツを勝手に利用している時点でかなりグレーだった。 ただ今は規約が追加され、作者と実況者の間で受け入れられるようになった。 ここで過去の作品にも作者が条項を追加できているのは、重要なポイント。 このへんの詳細は不明だけど、イラスト生成にもこのルールを適用すると、後から駄目と作者に言われたら再学習しないといけない。これは大変。

個人の権利がより重要になる

勝手に画像を使って、勝手に画風を真似てもフェアユース? それはないと感じる。 検索エンジンは robots.txt などのブロック手段を用意したから許されているので、イラスト生成はその観点が抜けている。 強力に個人の権利を侵害する可能性があるので、意図的な収集は GDPR より問題と思う。 AI の再学習コスト的に、AI のハードルになりそう。 仮に OK が出ると、それはそれで凄いことになりそう。AI なら何やっても OK なんだな、と。

概念の扱いがポイントになりそう

ChatGPT では意図的に固有名詞を深く扱っていないように見える。 イラストでもこれは使える。初音ミクという概念を学習しても、その語彙を意図的に「女の子」などに置換して画像を生成した場合、それは著作権的にどうか。 つまり危険なワードを近似ベクトルに置換してしまえば著作権的に OK ではないかと言われると、そうかも知れない。人間も頭で同じことをやってるしね。 この方法を使うと、データも違法な方法で取得したかは判別不能そう。まあでもイラストはタグで Danbooru とわかるよね。このへんの扱いは気になる。 Danbooru は現状で問題しかないけど、それをクリアすれば直感的にセーフになりそうな気がするので、やはり技術的には止まらなそうか。 ただ最近はプロンプト攻撃 (禁止ワードを無理やり作り出す) なるものがあるらしいので、そんなに簡単ではないかも。

現時点でもイラスト AI は 95% 以上の人間より優秀

5% の領域 (概念の不明瞭な領域、手などの細かな特徴) などはすぐ修正される。 この 5% の領域もすぐ 2% くらいまで行くので、人間はほぼ勝てない。

AI は人間の上位存在として対策することが前提になる

人間と AI を区別しないと、予想 5年で人間がやってられなくなる。 やってられなくなるからこそ区分けが必要になる。 結果だけからでは AI と人間の判別は不可能なので、囲碁・将棋で言うソフト指し対策が全分野で基本になる。

人間より高速なのでコンテンツが溢れ返る

AI はコンテンツをものすごい速度で作ってしまう。 そのせいで乱雑なネットからの検索は汚染されて不可能になるし、これは既になっている。 テキストと画像はもう完全に駄目かも知れない。 人間と AI を区別した上で、さらにビジネスモデルにも変化がいる。

予想 5年で人間は人間以上になる

生成モデルを利用した人間の進化は向こう10年くらい凄いことになりそう。5年以内に人間はすごく進化する。 それなりのものはすべて一人でできるから分業が難しくなる。 どう変わるかというとプロは必要だから残るけど、アマが猛烈に強くなる。ゲームや囲碁・将棋がわかりやすい。

人間以上になるためにはお金が掛かる=つらい

人間の性能を抜き去ろうとすると GPT-3 のように個人で扱えなくなる。 金銭面に限界があるので向こう 5年くらいの普及は限定的かも。イラスト月額1,500円はプロ用価格。 ハードとアルゴリズムが進化して安くなってからが本番。時期はあまり読めない。

著作権と商標権がもう少し真面目に扱われるようになる?

最近の AI 界隈がやってる著作権のガン無視はさすがにちょっと気になる。 そして 開発者のこのような考え方 にも賛同しかねる。 AI の人間超えが当たり前になったことで、人間以上の規制をしないと迷惑が大きくなっている。 AI が作ったものに著作権はない、は今後あまり通用する気がしない。

大量生成されたコンテンツによる悪影響が顕著になる

既になってるけど、自信を持って 5年以内にもっとひどいことになる。 AI が大量のゴミをネット上に生成して、コンテンツが爆発して管理できない時代になる。 著作権や商標権などは拡張が必要。逆に言えばきちんとお金を払えば Win-Win そう。 コンテンツの多い日本は、他国が AI したらチャリンチャリンな状態にしなければいけない。

生成モデルの限界は資金面以外ではたぶんない

できないことは人間の成長と共に概念を拡張すればいい。 それも限界となったら囲碁将棋と同じように自己対戦の時代が来るし、もうやってる。

AI は分野をすぐ飽和させる

AI は歴史をかっ飛ばして答えを一気に出すビジネスモデル。 歴史がないからすぐにユーザ数が飽和してあまり伸びない。最後の進化は細かい微調整のみ。 OSS 化が猛烈に早く、リリース時点で 95% 完成されているから頭打ちが早い。

分野の変化は囲碁・将棋ソフトがわかりやすい

アプリに組み込まれ、売上はフラット化して一定層のコアユーザが課金していくモデルが多くなりそう。 イラストは一般人が入りやすいのでユーザはより伸びそうだけど、課金が高くなりそうなのでプロ向けになりそう。 アマは安くして欲しい (願望)。

AI はすぐ飽きる?

AI イラストは長く流行るかと思ったら、すぐ飽きた人も多いみたい。 パターンを変える方法が少ない、難しいことが原因そうなので解決はできそう。 ただしすぐに作れる=飽きるが根源の場合、重要なのは過程を楽しむことだとわかる。

一般回答は既に人間を超えている

最近出てきた対話 AI は、高度回答は難しい反面、一般回答は人間並、コードも書ける模様。 高度回答もイラストのように特化させればできそうなので死角はない。 ただし平気で嘘を付くので、依存しすぎるのは危ないと思う。

検索より対話、対話より…

ネットがさらにゴミで溢れると、検索より対話のほうが早くなりそう。 そんな時代になると、対話の進化も止まる。検索や対話で確認できないことに価値が出てくる。 特に新しさや、高度な理解をもって実演できることは大切になりそう。

人間の仕事を考える必要がある

条件に合った薬を選ぶ、画像を分類するなどの解答がラベルの問題はもう人間は勝負にならない。 イラストを描くことも、90% の人には AI が勝てる。 自然言語処理も、前から人間レベル。 ChatGPT はアメリカの司法試験は合格できて、医師国家試験でも良い点が出ている (12/12)。筆記試験はオワコン。 プログラミングは今のところプログラマ の中で 50% くらいの実力らしい。 一般人基準だと 90% くらいの数値になるんじゃないか。 もう並の人間よりはどんな分野でも AI のほうがつよい。

でも今の AI は事実以外はうまく扱えない。たとえば AI に曖昧性の高い歴史研究などは当分無理そう。 ゴミデータ問題も今後重くのしかかって来る。課題はある。 あと恣意的・政治的な学習をされても外部から判断が付かないし、思考誘導される可能性が高いのは危ない気がする。 もっともたいていの検索エンジンが恣意的・政治的なのは報道されてわかってるので、大差ないかも知れないけど (2/18)。 他にも算数は苦手らしい。 まあ完全性を求める分野なのでそうかなという気はするけど、公式に特化した研究もされているので、絶対的に苦手な領域とは思わない。

イラストを描いたり、プログラミングをさせたり、解答を考えさせるのも一般人以上は間違いないけど、これも限界はわかりやすい。 コンテキストが少なくて良い常識的な問題は、データもたくさんあるので極めて強い。 一定レベルはポンと出るけど、それを完璧なものにしようとすると、説明コストはどうやっても高くつく。 これはフレーム問題の延長線だね。 画像生成の呪文も大変だし、プログラミングを丁寧に説明するのは地獄で、もはや自分で書いたほうが早い。 結局は、AI 以上に分野を理解していることが大切なのは、まだまだ変わらないのだろう。

5年くらいで色々なものが大きく変わりそうな印象です。すごいなー。

2022年11月24日木曜日

MIDI を楽譜に変換する midi2abc を作った

MIDI を使ったアプリを作りたかったので、その前準備として、MIDI を楽譜に変換する midi2abc を作りました。



楽譜を自動生成する利点

楽譜は MIDI から変換せず、手作業で作成したもののほうが確実ではありますが、自動生成にも利点があります。 まず、(1) 楽譜のフォーマットは非常に種類が多く、とてもすべてサポートできるものではありません。 また既にほとんど使われなくなったアプリの独自フォーマットも多く、サポートしても今後使い物になるのかという問題が起きているケースもあります。 有名な楽譜フォーマットでも相互変換もできないことがほとんどで、現実的には MIDI に変換してそこから他の楽譜フォーマットに変換することが多いです。 この点に関しても特定のアプリに依存しすぎている問題があります。 それなら MIDI から楽譜を自動生成する基盤を整備したほうが良いように思います。

(2) フルスコアと楽器ごとのパートスコアの動的生成に対応できます。 MIDI の 127 の楽器をすべて使ったフルスコアは、楽譜がカオスになって使いものにならない問題があります。 そのようなとき、現実的には DTM のような表示にするか、楽器を限定したパートスコアへの変換が必須と思うのですが、 生成済みの楽譜をベースにしたサービスでは取り扱いが難しいことに気付きます。 自動生成すれば対応しやすいです。 そしてこれは変換ツールというよりフロントエンドの役割です。

(2) 楽譜は曖昧性が高いフォーマットなので、MIDI から変換しようとするとどうやっても不完全になります。 ただ逆に言えば、重要性の高い記法だけを利用した楽譜に変換することで、シンプルになります。 古い楽譜を見ていると今は使われなくなった記法が多々存在しますが、そういったものをシンプル化することができます。 読みやすくなったり学習しやすくなる効果が生まれそうです。 既に最もシンプルな表現になるような実装ではあるのですが、 将来的には楽譜の難易度を変える機能なども作れるかも知れません。 楽譜の生成が完璧でなくてもこのような新たな価値を提供していくのはアリかもと思っています。

楽譜表示ライブラリは abc.js を採用

楽譜を表示するライブラリはいくつかありますが、 abc.jsOpenSheetMusicDisplay (OSMD)、Verovio のどれかが良さそうです。 本当は Magenta.js の楽譜機能が MIDI から楽譜を生成できて便利なのですが、 残念ながら現時点では実用に耐えません。 そして OSMD、Verovio には MusicXML で楽譜を表示する以上の説明がなくてつらいので、 発展的なアプリは作りにくいです。 そこで仕組みがわかりやすい abc.js を採用しました。

abc.js を採用した理由は API の充実度以外にもたくさんあります。まとめると以下のような理由があります。 (1) abc notation はファイルサイズが小さく、仕様がわかりやすく、パースしやすいです。 MusicXML は手作りしたくない一方で、abc notation なら手作りできそうでした。 (2) 表示される楽譜が綺麗です。OSMD / Verovio も同レベルに綺麗ですが、その他のライブラリと比べるとやはり大きな利点と言えます。 (3) MuseScore / MusicXML は外部アプリケーションの依存性が強すぎて自由度が低いです。 たとえば MusicXML を生成するためには今のところ MuseScore が必要と思うのですが、必要としてしまうとそこからの発展性がないので依存はあまり好ましくありません。 自由度が非常に高い abc notation をさらに使いやすくしていくことのほうが、OSS としての価値を感じました。 (4) abc notation で既に作られた資産は他と比較して非常に大きいです。 消滅すると明らかに困る資産です。

MIDI 解析は Mageta.js を利用

MIDI から楽譜への変換は、abcmidi に含まれる midi2abc (C言語) を利用すれば、 パート数の多い複雑な楽曲以外は割と綺麗に変換できました。 そのため wasm 化して利用させてもらえば、そのまま使えそうにも思いました。 ただコードを読んでいると、wasm に変換するとき file read がどうなるかよくわからないツラミがあり、 また MIDI 仕様への依存が大きく、JavaScript 脳の私には処理がわかりにくいと感じました。 楽譜生成した後にも楽譜を動的に調整できたほうが嬉しいと思ったのですが、 その時に wasm だと苦しそうな気がして、自前で実装することにしました。

具体的な方法としては、Magenta.js が MIDI をパースして Tone.js の NoteSequence に変換してくれるので、 NoteSequence を通じて MIDI と abc notation を相互変換することにしました。 この方法なら MIDI の解析部をパスして実装できてわかりやすいです。

楽譜への変換処理は、実装してみると細かな制約があることにも気付きましたが、許容範囲でした。 正確に確認した訳ではないのですが、公開時点ではクラシックならほとんどの MIDI は良い感じに再生できる印象です。 ゲーム音楽くらい複雑になるとまだまだな感じです。 あと聴くだけなら耐えられても、楽譜としては見るに耐えないものもあるので、まだまだ改善が必要です。

TODO

今のところの課題は正確な連符処理、装飾、転調などです。 他にも MIDI ファイルの楽器や楽譜の扱いが不十分だと楽譜がカオスになる問題はあります。 綺麗な楽譜を出力するにはそこが一番のハードルになるだろうと思っています。 そこまで作り込めば楽譜としてもほぼ完璧なものが出力できるようになると思いますが、 その後にも歌詞表示、ループ解析、小節解析、ドレミ表示など、やりたいことはたくさんあります。

100% の完成度を目指すといつ公開できるかわからないので、α版で公開しました。 コードは安定していないので利用しないほうが良く、ひとまず基本的なバグがなくなった状態です。 α版とは言っても簡単な曲なら綺麗に楽譜にできるはずです。

2022年10月21日金曜日

JavaScript で autocomplete をいい感じに作る

JavaScript で autocomplete をいい感じに作る方法をまとめました。 今回ターゲットにしているのは、文字列配列を、前方一致検索で、手軽に、いい感じに autocomplete する方法です。 最近の HTML/JavaScript は進化しているので、autocomplete / suggest / incremental search と呼ばれる類の UI を作ることは簡単になっています。 反面、ベストプラクティスはわかりにくくなっているのでメモっておきます。

1. HTML の標準機能を使う→駄目

まず最近私がやっていたのは、HTML の input をそのまま使って、list 属性に datalist の ID を指定する方法です。 MDN に良いサンプルがあるのでそのまま引用します。 ライブラリを使わないでも作れるので手軽で、動作も非常に軽快です。
<label for="ice-cream-choice">Choose a flavor:</label>
<input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice">

<datalist id="ice-cream-flavors">
    <option value="Chocolate">
    <option value="Coconut">
    <option value="Mint">
    <option value="Strawberry">
    <option value="Vanilla">
</datalist>


もうこれでいいじゃんと思っていた時期もあったのですが、データ数が 3000 を超えると PageSpeed Insights が、 「過大な DOM サイズの回避をしてください」と怒ってきます。 この仕様は不満ですが仕方ない。データ数が増えたら DOM に依存しすぎないライブラリを使うべきのようです。

2. 高速なライブラリを使う→意外とない

autocomplete / suggest / incremental search といったキーワードでライブラリを検索すると、たくさんのライブラリが見つかります。 だいたいは autocomplete で探すと良いですが、index search なども対象に入れると良いかもしれません。 見つかった色々なライブラリを試してみたのですが、多くのライブラリはデータ数が 1万を超えると壊滅的に遅くなってしまう問題があります。 有名なものも、古めのライブラリはほぼアウトです。 高速なライブラリが求められます。 速度に関して今のところ最も参考になるのは、最近登場した uFuzzy の benchmark ですが、それほど多くはまとまっていません。 また fuzzy search なので参考程度です。だいたい実装してみるしかありません。

3. UI が使いやすくカスタマイズ性の高いライブラリを使う

そしていざ autocomplete を実装しようとすると速度だけでなく UI を作るほうが面倒なことにも気付きます。 高速で簡単に UI を作ってくれるライブラリが欲しいです。 改めてライブラリを探してみると、まずはJSON で構造化されたデータから検索したいなら minisearch などがわかりやすいと思いました。 しかしただの文字列配列には対応していないように見えます。また同じような条件のライブラリが多く、意外と良いライブラリが見つからなくて困りました。 autocomplete のライブラリは UI が特殊なライブラリも多かったのですが、昨今の CSS Framework 事情的には、選択候補だけ表示してくれるようなものが良いと思います。 たとえばこのへんのライブラリをチェックしていました。
最終的に一番良いなと思ったのが、kraaden/autocomplete です。 基本は JSON 形式へのデータを対象としていますが、ただの文字列配列にも適用でき、処理をフルカスタマイズでき、高速に動作します。

実装例

実装例もまとめておきます。複雑な設定がないので凄くわかりやすい。 このコードを見ればだいたい何にでも対応できそうなことがわかると思います。
autocomplete({
  input: input,
  fetch: function (text, update) {
    const suggestions = tags.filter((tag) => tag.startsWith(text));
    update(suggestions);
  },
  render: function (item) {
    const itemElement = document.createElement("div");
    itemElement.textContent = item;
    return itemElement;
  },
  onSelect: function (item) {
    input.value = item;
    searchIcons();
  },
});
kraaden/autocompleteIcon Search で利用しています。

追記: 試してないけど、trevoreyre/autocompletealgolia/autocomplete も良さそう。

2022年10月4日火曜日

タイプ漢字読み取りを作った

タイプ漢字読み取りを作りました。 出題された漢字をタイプで回答できる、漢字練習アプリです。



以前作った 手書き漢字読み取り漢字読み取り音読 のタイプ版です。 技術的には難しい要素はありません。 UI をメニューで切り替えるよりはアプリがたくさんあったほうが UI がシンプルになるので、アプリを分けています。

英単語は打鍵数が多くなるのでこの UI だとやや微妙ですが、日本語は打鍵数が少なく一定になる傾向があるので、タイプ入力は案外適しています。 ローマ字のだいたい 1/2 の量で入力できるのですから、そりゃそうかという気も。 入力速度は、漢字読み取り音読 には劣りますが、手書き読み取り音読 よりは高速です。 電車スマホで遊ぶにはちょうど良さそう。

2022年9月8日木曜日

テキスト変換ツール text-utils を作った

テキスト変換ツール text-utils を作りました。



テキストエディタを使っていると、ちょっとした操作をサクッとこなせるメニューが欲しくなることがあります。 たとえば普段よく使っている vim で苦手なのは、正規表現のエスケープです。 他の仕様とズレてて使いにくい。他にもゼロ埋め連番などは毎回困ります。 Linux コマンドで一番苦手なのは、cut コマンドで列を抽出するコマンドです。 暗記していたら時々便利だけど、覚えていません。そんな機能はたくさんあります。

どれも JavaScript で処理するコードが簡単に作れるとわかっていても、 たまに使うくらいの場合コードを書くのが面倒ですし、案外時間も掛かります。 検索エンジンで関数やコマンドを調べるのも面倒です。 Web 化の進んできた昨今では、そろそろ Linux コマンドに依存する必要もなくなってきています。 事前にメニューを用意しておいて、ワンボタンで実行できる Web エディタが欲しいなあと思って、ノリで作りました。 ただノリで作った割には、自分でもかなり使っている気がします。

最低限ほしかった機能はサクッと作ったので公開しました。 他の変換ツールと違って JavaScript を編集して実行できるようにしておいたので、上級者にとっても便利なルールになっています。 地味につよつよな機能が搭載されていたりもするので、割と使い勝手は良いはず。 何か欲しい機能があれば、追加します。 たまに見かける MD5 / SHA1 みたいなハッシュ値ツールって、私は最近一度も使ったことないので作らなかったんですが、使う人いるのかな?

一度作ってしまえば今後は調べる必要がないので気楽ですね。 ツールとしても便利ですが、サンプルコードとしても使いやすいです。 あまり難しい実装はないので、実装の確認にもちょうど良かったです。 絵文字だけはミスりやすいので要注意だなと感じます。

2022年9月2日金曜日

手書き漢字書き取りを作った

手書き漢字書き取り を作りました。 昔から作ろうと思ってはいたものの、ハードルが非常に高いので後回しにしていたアプリです。 個人開発では、漢字認識、熟語生成、読み方解析、文章生成の 4つの基礎技術が必要になるので、なかなかハードです。 最近公開した 80こくらいのアプリの中では、どう考えても一番難しい。



問題文にふりがなをふるのが難しい!

まず漢字の書き取り問題を出題するには、まず熟語などのリストをもとに、問題文を作らないといけません。 つまり文章生成という大問題にいきなりぶち当たりますが、wncc-ja でそれっぽいものを作ったので使います。 しかし次の大問題として問題文にふりがなをふる必要があります。 読み方が複数ある漢字があるため、ふりがなをふるのも難しいことに気付くかと思います。

最初は wncc-ja で作った例文を使えば良いと思っていたのですが、 「入り (はいり)」と「入り (いり)」の区別が難しくて、正確な出題が難しいです。 「難い (にくい)」と「難い (がたい)」のように意味が変わらなければ良いのですが、 「聖人 (せいじん)」と「聖人 (しょうにん)」のように意味が変わるケースもあります。 形態素辞書だけではどちらの読み方が正しいか判別不能そうで、正確な読みを取得することが困難です。

改善できる?

そこで複数の読み方が存在する語彙を除外してみました。 ちなみに複合語になったときに濁点の数が変わるようなものはそれっぽく除外しないと酷いことになるので注意は必要です。 一応上記の解決案で何とかなるとわかったものの、多少出題範囲が狭くなるため、他の解決案も模索しました。

まず根本的には既存の形態素解析の枠組みでは、正確な読みは推定するしかないところに問題があります。 基本語彙を使ってしまうと読みに重複が発生するので、重複が発生しなくなるまで語彙の基準 (SudachiDict) を A→C まで上げればよいかとも考えました。 読みに重複がなければ問題として使いやすいですし、なにより意味が一意に近づくということでもあるので、一見良さそうに思いました。 しかし基本語彙以外を使ってしまうと、役に立たない語彙が一気に増えてきて、あまりにも微妙でした。 勉強するなら誰しも、効率よく、正しく勉強して欲しいですから、駄目っぽいですね。

役に立たない語彙を増やさないよう、読みが重複したときだけ形態素解析辞書でするとどうだろう、とも考えました。 たとえば「仮名」には 4つの読み方が存在しますが、「かな/がな」以外の読み方は複合語では利用されません。 「かな/がな」の複合語だけ対象にすればいけるかなと思いました。 ただ SudachiDict でいけそうか軽く確認したところ、あまり使える気がしなかったので、諦めました。

やはり基本語彙以外は基本的に実用には難しい、と考えたほうが良さそうです。 間違っている可能性があっても出題するか、すべて削除するかですが、わたしは安全を取って後者にしました。 BERT も GPT-2 ももちろん駄目だし、どうしたものかと小一日ほど悩みながら色々検証した結果、 やはり wncc-ja が一番良いと結論付けました。難しいね。 何週間か寝かせて他に方法がないか考えましたが、現状ではこれが一番の気がします。

なんとかできた

基礎技術がてんこもりでハードルの高いアプリでしたが、なんとか成し遂げました。 現状の技術で突破できるギリギリを攻めているため、細かな点には課題があります。 改善するには、読みが一意になる条件をまとめた辞書を作るか、wncc-ja を改善するか。 どちらも地味な作業が必要で超難しく、Deep でポンの解決はできないです。 逆に言えばこのへんの研究を進めると形態素解析の精度も上がりそうというのが予想できます。

課題は多いものの、完成したアプリを遊んでみると結構いい感じです。 問題文が微妙な可能性を考慮して 問題文を変えられるようにしたのですが、普通のテストにはない機能でなかなか良いです。 巷の漢字テストも問題文がわかりにくい時があるので、これなら迷わなくて済みます。

2022年8月25日木曜日

Icon Search の DB をつよつよにした

以前作った Icon Search のDB をつよつよにして、Cloudflare Pages に移行しました。



DB が爆発 そして Cloudflare へ

移行の理由は単純で GitHub Pages の上限に到達したからです。 最近まで icon-db のサイズは 630MB くらいで、GitHub Pages の上限はまだまだ余裕と思っていたのですが、 最近登場した Fluent Emoji の容量が凄まじく、ローカル環境で一気に上限の 1GB をオーバーしました。 このせいもあって改良に滞りが起きていました。 今後も高画質なアイコンが出てくると、思ったよりファイルサイズの増大が早くなるかも知れません。 これは何とかせねばと思い Cloudflare Pages への移行を決めました。 Cloudflare Pages なら 50GB まで運用できるので、まだまだやれます。

移行にあたっては 2つの問題をクリアする必要がありました。 Cloudflare Pages は (1) 20,000ファイルまでしか管理できず、(2) 25MB 以上のファイルは管理できません。 どちらもバックエンドには重要な問題で、ファイル数は inode 枯渇問題、ファイルサイズは CPU/メモリ/ネットワークの圧迫に繋がります。 Cloudflare は CDN 屋さんですから、この 2点へシビアになるのも頷けます。 逆に言えばシビアだからこそディスクの容量を他より大きくできると言っても良いと思います。 フロントエンドの価値が高くなると、その傾向が強くなるかも知れません。

JSON DB を改良した

先ほどの問題を解決するためには JSON DB を改良しないといけません。 今回魔改造した DB を以下にまとめます。 まずバックエンドでは、JSON DB を以下のように管理します。
  • 10MB を超えたら JSON を分割して fetch (a.json --> a.1.json, a.2.json)
  • 5KB 以下のファイルは可能な限り 10MB 単位でまとめて Accept-Ranges fetch
フロントエンドでは heavy.json, light.json を最初にロードします。
  • heavy.json: 10MB を超えるタグのリストと、ファイル分割数
  • light.json: 5KB 以下のタグのリストと、Accept-Ranges のポインタ

heavy.json, light.json のタグにマッチする場合は、fetch の方法を切り替えます。 すべての fetch 情報を事前に読み込んでも良いのですが、たぶん 1MB を超えるのでやや微妙です。 他にも機能を追加したいとも思っているので、転送量は増やさないようにします。 また DB のファイル数を減らそうと思えば徹底的に減らせるのですが、どれだけ減らせるかはあまり意味のない議論です。 これは Accept-Ranges fetch より通常の gzip 圧縮の効いた fetch のほうが効率的だからです。 ほとんど使われないタグを Accept-Ranges fetch にして、使われるタグは通常の fetch にするほうが効率が良いです。 なんにしても、これで最初に 300KB ほどの heavy.json, light.json をロードするだけで、 後は O(1) で検索できるようになりました。ファイル数も 10,000 減って管理しやすくなりました。

割と最強の静的 DB なのでは?

sql.js-httpvfs の仕組みにかなり近づいているので比較すると、以下のような利点があります。 (1) JSON なので git で管理できる、(2) 将来は一緒になるかもしれないけど現状では通信量が〜1/3 で済む、(3) O(1) で検索できる、 (4) 1.5MB の sql.js-httpvfs をロードしないで済む、(5) 難しい実装に依存せず改良できる、 (6) 不均衡データの実装がしやすい、(7) 10MB 単位以外のブロックにも設定変更しやすい、 (8) Accept-Ranges fetch するだけで転用できる。

欠点は (1) タグ数が増えて light.json の容量が大きくなった時に通信量で困ること、 (2) light.json に関連するデータが same-origin でないと動かないだけのように思います。 欠点があるとはいえ sql.js-httpvfs の容量がかなり大きいので、light.json でタグ数 2万くらいまでは普通に管理できます。 ほぼ JSON DB のほうが有利です。 10万くらいになると大差なくなるので sql.js-httpvfs を利用するべきかも知れませんが、 アイコンに関しては超えるとは思えないので、JSON DB が正解そうです。 英語に最適化すればたいていのデータにも適用できるので、意外と応用も効きます。

雑に作った JSON DB も、気付けば一丁前の DB になってきました。 フロントエンドでは write が不要なので自前でも実装でき、 また SQL を使う必要性もないので、複雑な実装も必要ありません。 案外このような DB が最強かも知れません。

DB をつよつよにしたついでに、細かな機能もつよつよにしておきました。

キーワードが浮かばない人のためのコマンドを作った

DB の改良によって類似検索の真似事のようなことができるようになりました。 低頻度のキーワードをまとめた JSON ファイルを作ったので、その JSON ファイルを検索できるようにしました。 検索キーワードが思い付かない人は、キーワード @rare で検索すると、大量にいろいろなアイコンが見られるので、良さげな SVG を簡単に探せます。 類似検索なども作ろうかなとは思ってはいるのですが、 何もキーワードが浮かばないなら一度にたくさんのアイコンを見て、 そこから良いものを選ぶほうが早いかも知れません。 低頻度のヒットしないアイコンにも目が向きますし、なかなか良い案と思いました。

タグ補完を頻度順にした

低頻度のタグが補完で邪魔してくるのが、個人的には多少気になりました。 そこで頻度順に表示してみたところ、結構使いやすくなった気がします。 頻度順に表示しているのでキーワードに困ることも減るかも知れません。 頻度が低すぎるタグは削除してもいいような気がしましたが、ひとまず残しておきました。

SVG の ID 重複排除を高速化した (その1)

Fluent Emoji をレンダリングすると、かなりのアイコンで時間が掛かることがわかりました。 原因としては ID の重複排除ですべての SVG 要素をチェックするときに、ID ごとにすべての要素をチェックしていました。 ID 数と要素数が多くなると酷いことになります。 そこで一度だけすべての要素をチェックして、ID が含まれていそうな要素と属性をリストアップし、 そのリストに対して ID 重複排除処理を実行することで、15倍ほど高速化しました。 ID 重複排除の実装は何度も書き直している気がします。案外難しいですね。

SVG の ID 重複排除を高速化した (その2)

Fluent Emoji はそれでも重い SVG がありました。 特に重い SVG を開いてみると、要素数が 1万、ID も 3000 近くあり、DOM に書き込むことすら時間がかかるし、cloneNode(true) するだけで 1秒近く掛かります。 色々計測してみると、属性名を複数回に分けて置換している処理が重いとわかりました。 ID が 3000ともなると複数回に分けて置換するだけで大変なことになります。 そこで SVG のフォーマットを事前に整え、正規表現で処理してみたところ、100倍ほど早くなりました。

SVG の ID 重複排除を高速化した (その3)

アイコンの表示は十分な速度が出るようになったのですが、今度はアイコンの詳細を表示するときが問題になりました。 要素数が 1万ある SVG をcloneNode(true) したり、再描画しないとアイコンの詳細を表示できないのですが、それだけで 1秒近く掛かります。 これは DOM ノードが多すぎることが原因なので、DOM ノードを減らすには Data URI などを使った表示にすべきという結論になりました。 そういえばその技が使えたな…と気付いてしまったので、いろいろ豪快に実装し直しました。 Benchmark 記事も見つけたのですが、これが一番早いらしいです。 苦労して作った ID 重複処理がいらないとわかったのはショックでしたが、良い気付きになりました。 ちなみに何も考えずに appendChild すると体感的に遅くなるので、XML Parser を使わなくても無理やり Worker に投げて描画する実装は残しています。 初めての人が見るとまったく意味がわからないコードなのですが、Promise もやや遅く感じます。 このへんの実装ってどうするのが良いんですかねえ。

何にしても、この変更でアイコンの詳細表示は耐えられるレベルになりました。 ちなみにまだ微妙に重いためもっと非同期に動作させたいのですが、どうにも難しいです。 原因としては cloneNode の代わりに img.src を使っていて、原理上は async に動くのですが、内部では Data URL のパースで DOMParser が sync に動いてそうなところに問題がありそうです。 直感的には画像のキャッシュがあるので、ctx.drawImage(img, ...) すると高速そうとも思ったのですが全然駄目です。 既にレンダリングしている画像の width/height だけ変えてパースを回避することでごまかそうとも思いましたが、これさえ重いので、width/height を変更した時の再レンダリングが予想以上に大変なのかも知れません。 いずれにしてもあまり良い解決案はなさそうです。 まあ、そもそも要素数が 1万 あるアイコンはさすがにちょっとなと思うので、path で書いて欲しい気はします。

レンダリング順序に自由度をもたせた

DB を 10MB のブロック単位の JSON にしたことで、レンダリング順序に自由度をもたせることができるようになりました。 これまでは一つの JSON にまとまっていたため表示順序に損得が生じており、古めのアイコンのほうが得をするようになっていました。 データ数が増えた時に顕著になっていましたが、ブロック単位にして、ブロックごとにランダム表示するようにしました。 これによって、より様々なアイコンを見つけやすくなったと思います。

今後もちまちま改善を続けたいと思っています。

2022年8月8日月曜日

漢字読み取り音読を作った

漢字読み取り音読を作りました。 出題された漢字を音読することで回答できる、漢字練習アプリです。



タブレットがあれば 手書き漢字読み取り も良いですが、 スマホだと UI が厳しいので音読できると良いなと思って作りました。 手で書いたほうが頭に入る気がしますが、解答可能速度はこちらのほうが圧倒的に上です。 海外の日本語学習者の方が、発音の練習に使うことも意識して、多言語対応もしています。

以前作った ダジャレ音読早口音読 の内部で使っている、読み方解析器を使っています。 そのまま転用できるかと思ったのですが、データのパターンが数百から数万まで増えるとさすがに見逃していたバグが出てきたので、きっちり直してからリリースしました。 こういったゴリゴリ系のアルゴリズムは何百回もテストしながら作る必要がある上、最近ほとんど見かけなくなったので割と苦労しました。

問題の解決には、結構色々な基盤ライブラリの改善が必要でした (下)。 ちなみに応用アプリのほうもすべて更新済みです。 形態素解析の仕組みを隅々まで理解するのは大変なので、やはりクロスチェックしないとなかなか良いものは作れないですね。 そしてチェックすればするほど SudachiDict の使いやすさが際立ちます。
読み方解析の精度は 100% なのですが、音声認識で特有の認識ミスが発生するので、多少精度が落ちます。 たとえば「量産→リョーサン」のように「ー」に書き換えてくるケースはそれなりにあって、これは認識に失敗します。 あと形態素解析辞書には登録されていない、間違いの読み結果「差し出し→差し出」を返却されると認識に失敗します。 間違い方は 以前書いた記事 とだいたい一緒です。 体感認識精度は 95% でした。

「ー」の問題は、今回のような単語単位のマッチングなら先頭以外の「あいうえお」を「ー」に変換して完全一致検索すれば良さそうだったので、対応しました。 これで体感認識精度は 98% くらいになったような気がします。 音声認識の間違いは SudachiDict でわざと使っていない誤用ルールを使えば部分的に対応できるかも、 ただ完璧は難しそうだし、面倒くさそう。 許せる認識率にはなっているので、ひとまずは良いかな…。

最近の音声認識技術は適当に考えても読み方解析は弱いので、個人的にはひらがな/カタカナで返却する API が欲しいところです。 ただ日本語の優先度は低いでしょうから期待はできません。今回作ったような仕組みで対応するのが一番良さそうです。 日本語の音声認識の応用は、簡単そうに見えても作り込むのが割と難しいです。

2022年7月28日木曜日

手書き漢字読み取りを作った

手書きで漢字の読み取り練習をするアプリを作りました。



漢字の学習はいくつかのレベルに分かれることに気付きます。 (1) シンプルな反復の練習、(2) 文字を綺麗に書く練習、(3) 考える練習です。 1 である程度覚えた上で 2, 3 に取り組まないと効率が悪い気がしますが、 私の作ったアプリは 1がやや弱い気がしました。 タッチ漢字ドリル はカリキュラムとしては使えるのですが、やや手軽さが不足します。

カリキュラムより反復練習

多言語学習について目を向けると、漢字圏以外では 2 が存在しないことが多く、 よりシンプルな反復練習が大切になります。 反復練習せずできる子もいるけど、できない子のほうが当然多いし、反復練習しないが故にできなくなる子のほうが圧倒的に多い訳です。 どんな言語でも 1 をサクサクと反復練習できる基盤が大切だし、ほしいと思いました。

今回のアプリを作るに当たって閃きがあったのは、やる気が出ない問題への対処です。 子供に限らず私のようにやる気のない大人にも言えることは、 カリキュラムの存在はやる気をなくす原因になる事実だと思います。 教える側としてはカリキュラムがあると何も考えなくていいので非常に楽ですが、 やる気がない側から見るとそびえ立つクソにしか見えず、逆効果になりがちです。

勉強が苦手な子ほどやる気の問題があることも多いので、やる気とは別に量をこなす仕組みが必要です。 散発的にでも反復していれば多少は覚えられるので、そのようなアプリにしました。 色々と UI を考えましたが、結局は 英単語クイズTegaki PhonicsTegaki ABC の漢字版みたいなところに落ち着きました。 意外と UI が難しいんだよなあ。

日々の練習アプリとして良さそう

手書き読み取り漢字は習っていない漢字を学びながら練習するアプリとして考えていて、 1日3分間くらい練習するのに良さそうと思っています。 ある程度は頻度を考慮しているので、さほど効率も悪くはないです。 テスト代わりとしても使えそうですが、全員ランダムな出題となるので好みは分かれそうです。

認識精度も良く、手書きはタブレットならまあまあ使いやすいけど、スマホで扱いにくいのが課題でしょうか。 最近の子はみんなタブレットを持っているので気にするほどではないかも知れないですが、スマホ用にも何種類か他の UI を作る予定です。

2022年7月10日日曜日

Node と Deno と Bun

Node/Deno の代替として、Bun という高速な JavaScript 処理系が出てきました。どう見ても肉まん。 超期待ですが、Bun のプロダクト利用は、ドキュメントもないのでまだまだ無理そう。 あと Deno も進化しているので、Deno が疾くなるのと Bun の仕様とドキュメントが整備されるのでは、 どちらが早いかは微妙なところです。



Bun は実行速度が確かに疾そうで、3倍くらい疾い可能性はあるけど、いま表に出ているベンチマークでは比較に使いづらいところも色々ありそうです。 というか 3倍疾いと C/C++ の速度に匹敵するような…? HTTP Server はわからない部分も多々ありますが、 Deno の FFI と SQlite は遅いのがわかっているから、ベンチマークをそのまま鵜呑みにはしにくいです。 ただ Deno も Bun という良きライバルが登場したことで成長しようとしています。 たとえば FFI の高速化を検討し始めました。
deno-sqlite が遅い理由は前からわかってて、 API とサンドボックスの制限で WAL が利用できないから。 つまり Bun のベンチマークはサンドボックスなしの利点を考慮しないといけないし、 Deno もサンドボックスの欠点にはもっと議論が必要であるとわかります。 他にも利点欠点が better-sqlite3 のページに結構詳しく書かれています。 確かに高速性を追求するとメモリやディスクの共有は必要になりがちなので、 高速化領域ではこのような話が問題になりやすそうで、Deno の弱点かも知れません。
こちらの議論も活発化してきて、ブラウザの File System Access API を利用したり、 FFI 経由での高速化を目指すなどの話が上がっています (下)。 File System Access API でできるなら一番綺麗そうな気はするけど、Firefox が拒否しているからあまり進まなそうです。 DB や HTTP Server、その他でも高速が必要となると、read/write の多重化はたいてい必要になりそうで、 その結果セキュリティがどうなるかまでは考えてないけど、 言われてみれば確かにサンドボックスの拡張ができると嬉しいかも知れません。
ただ FFI をベースにした async-sqlite3 のベンチマークを見ていると、FFI が一番有望なのかもと感じます。 FFI が速くなればたいていの問題は解決しそうです。
あとこの結果を見ていると、言語として Python より早くても、read/write を経由すると遅くなる側面も見て取れます。 大規模な read/write に弱い、JavaScript (Node/Deno?) 特有の課題を感じますね。 やっぱり FFI の高速化は Node / Deno の責務なんじゃないかなあ。 その意味では Bun の目の付け所は上手いかも。

ここまでの話をまとめると、Bun が疾い話には Deno の制約の話が数多く含まれています。 Bun はそこをセルフ実装のパワーで解決しようとしており、 Node/Deno は互換性やセキュリティを大切に解決しようとしています。 Deno の進化も Bun の進化も、どちらもまだまだの状態なので、どちらも生暖かく応援しようという感じ。 プログラミング言語を作るのって難しいんだなあと、改めて思いました。

個人的には Deno と Node と Bun の関係は、Java と Scala のような関係とは異なると思っています。 基本的な根っこは同じで、また応用部分も ESM で相互運用可能になるはずで、長期的にはどれで書いてもそんなに困らないはずです。 そんな時に大事なのはシンプルさと速度ですから、Deno と Bun には注目です。

追記: 書き終わった後に似たようなことが公式でも書かれていることに気付きました。oh... 何にしても Deno も Bun もこれからですね。

2022年6月17日金曜日

gitn: たくさんのリポジトリをシュッと管理する CLI を作った

たくさんのアプリを作っていたり、たくさんのリポジトリを管理するアプリを作っていると、 たくさんのリポジトリをシュッと管理する CLI が欲しくなります。 一気に git pull して更新したり、一気に git commit したくなりますよね。

そこで gitn を作りました。 どうせ自分しか使わないので名前は適当ですが、 複数 (n) の git リポジトリに命令を投げるので、gitn (ぎっとぅん) です。

gitn

たくさんのリポジトリの管理というテーマ自体は昔からあるので、使えそうなものがないか事前に検索してみました。 ghq はもともと有名で知っていたのですが、条件を満たさなかったので他の CLI も探したところ、 gitbatch, gita, git-xargs が見つかりました。 ただどれもちょっと欲しい機能が違う。

リストを与えたら、シュッと処理してくれればそれで良いのです。 より正確には、 (1) 任意のディレクトリで実行しやすく、 (2) 任意のリストで実行しやすく、 (3) 自分の好みに拡張しやすくなっていることが大切です。 (4) またリポジトリのリストさえ持っておけば、自由に応用できると良いです。

gitn なら以下のようにできます。 コマンドは gitn [command] [to dir] [by list] で考えます。超かんたん。
gitn clone vendor/ repos.lst
gitn pull vendor/ repos.lst
gitn push vendor/ repos.lst
gitn status . repos.lst
gitn add . repos.lst *
gitn commit . repos.lst -m "comment"

ソースコードもシングルファイルで 100行ないので、誰でも挙動がわかります。

ちなみに Git には git submodule コマンドがあるので、バージョンをロックしたい時はそちらを使うと良いです。 しかし head しか使わない時とか、無駄な更新コミットで溢れかえらせたくない時とか、ロックする必要がない時には gitn が便利かも知れません。 また git submodule は死ぬほど遅いので、リポジトリが増えてくると結局似たようなスクリプトを書く必要が出てきます。 そんな訳で、リポジトリが増えすぎて submodule 化したくない時にも使えます。

2022年6月10日金曜日

えもじタイピング / Emoji Typing を作った

小さな子でも遊べる えもじタイピング / Emoji Typing を作りました。



作った理由として、小さな子だとタイピングが合わない子もいます。 わたしが作ったものだと、 ABCあいうえお九九 の次の 漢字 / フォニックス が小さな子はハードルになりそうです。

ハードルの低いタイピングがほしい

漢字 / フォニックス まで来ると、ただピコピコすればいい訳ではありません。 文字を読んでアルファベットを頭の中で変換したり、 漢字をひらがなに変換したり、知らない英単語を覚える必要が発生します。 何事も頭を使わない時期から、使う時期への切替はなかなか難しいものです。 小さな子にはそこが難しいのではないかと思っています。 もうすこしハードルの低い何かが必要です。

そこで漢字や英単語ではなくシンプルなひらがな/カタカナで、 絵が付いている えもじタイピング を作りました。 ひらがな/カタカナ→ローマ字への変換の練習だけは必要なので、シンプルにそこを練習します。 何度もゲームっぽくしようと思ったのですが、 ゲームっぽいのは意外と受けが良くなかったです。 理由を考えると、ゲームっぽくしたぶんだけ頭を使うからかも知れません。

ゲームっぽいのはまた別に作ればいいか、ということでとにかく平凡にしました。 時間も 120秒だと大人にとってすこしダレる感じがあるので、60秒のほうが良さそう。 2分だとやる気がないとできないけど、1分ならいつでもできるので、時間の空いた時にピコピコっとやらせる感じ。 60秒で集中力をもたせる代わりに、何度もプレイするような利用をイメージして作りました。 2回プレイしたいと思わせれば勝ち。 1.0 type/sec でも 8 こくらいの単語を入力できるので、まあそれなりには勉強になるだろうという思います。

英語や中国語の勉強にも使える

なんちゃって多言語対応しているので、Emoji Typing は英単語の勉強にも使えます。 初心者向けの単語しか含めていないので、小学生高学年の子の学習範囲に近いです。 小さな子なら文字数の少ない フォニックス のほうが良いと個人的には思いますが、 絵があるので小さな子もなんとかなるかも知れません。 個人的なイメージでは フォニックス の次にやると良さそうです。

実験的な試みとして、機械翻訳もサポートしています。 機械翻訳を利用しながら遊ぶと、日本語+英語、英語+中国語、日本語+フランス語のように、多言語学習ができます。 大人が多言語学習するのにもかなり良いです。 開発しながら中国語を勉強していましたが、気軽に覚えられてなかなか良かった。

2022年6月5日日曜日

Yomico: 半手動でふりがなのルビを振るライブラリを作った

半手動でふりがなのルビを振るライブラリ Yomico を作りました。 いまさらそんなものが必要なのかと思う人もいるかも知れませんが、 小さな子のためのページをたくさん作っているので、どうしても必要になってきました。



漢字に対してふりがなをルビで振る方法としては、真っ先に Mecab / TinySegmenter を思い浮かべるかと思います。 最近は Vaporetto もあるかな。 しかしそれらの技術では精度の問題があって、読み推定に関して言えば、残念ながらかなり気になるレベルです。 毎日見るようなアプリに適用するのは、さすがに抵抗があります。 形態素解析を真面目にやるとメモリ使用量や通信容量が大きくなる問題もあります。 といって手動でルビを振る作業は苦痛です。

そこでローカル環境で Mecab にルビを振ってもらい、事前にふりがなの候補をリスト化します。 そして間違っている箇所を手動で修正し、フロントエンド上ではボタン一つで、形態素解析器なしにルビを振れるような仕組みを用意しました。 この方式なら、95% の仕事は Mecab に任せられ、残り 5% だけを頑張れば良いです。 またふりがなの精度は常に 100% にでき、省メモリ・高速なルビ振りが実現できます。

かなり便利です。使う人は私くらいかも知れませんが…。

2022年6月3日金曜日

フロントエンド DB で運用コストゼロ

最近はサーバーレスの SQLite が人気みたいですが、個人的には sql.js-httpvfs が好きです。 個人開発で運用コストをゼロにしたいなら、こちらのほうが気楽です。

sql.js-httpvfs は Accept-Ranges を利用して、 DB のすべてのデータを fetch することなく、必要な時にバイト単位での fetch を実現します。 つまり DB をフロントエンドに置いた運用開発ができます。 バイト単位での fetch を実現する Accept-Ranges の仕組みは、フロントエンド新時代を支える技術になる気がしています。

静的 DB はフロントエンドへ移行する

ほとんどの開発において DB / ネットワークが最初にボトルネックになります。 昔から bytes-level fetch ができればフロントエンドに DB を置けるのにとは思っていたので、 Accept-Ranges fetch は超機能だなあと当時は思ったものです。 この Accept-Ranges 周りのサポートが最近一気に活性化してきました。

ちなみにこの技術を応用して フロントエンド で SQLite を動かす技術は 2019年に確立されていましたが実装も進化してきて、 また 2022年に主要なブラウザで使えるようになりました。 これによってバックエンドの DB がフロントエンドに移行する、新時代が来るかも知れません。 特に read-only で public な DB はバックエンドに置く必要がなくなります。 これはとても効率が良くて、うまくサービスを分割して公開すれば、だいたい無料で運用できるはずです。 多少の手間はありますが、迂闊にバックエンドを使って採算が取れず、コストが理由でサービスを供養するよりずっと良いです。

今のところ一番良さげな実装は sql.js-httpvfs ですが、 近い将来より良いものが出てくるかも知れません。 本当は KVS、しかも純粋な HashMap のように O(1) で使えたり、それに近い演算で処理できるライブラリがあれば嬉しいのですが、今のところないように見えます。 強い人がそのうち作ってくれるんじゃないかな…かな…。 まあデータ数が小さければ RDB の B+tree Index も KVS も大差ないので 私は sql.js-httpvfs を拝借しています。

とりあえず素振りした

そんな Early Stage に見える技術を使って、サクッと 8こほどアプリ / API を試作してみました。 いくつかテーマを付けて作ってみると、多少の課題もありました。 いま気付いている課題は、Prepared Statement と、コネクションの貼り直しですが、まあ何とかなります。 そのうち解決されたら良いですが、どうなるかな。

フロントエンド上では RDB の使い方は異なる

sql.js-httpvfs は SQLite の DB を 10MB ごとにブロック化して Virtual File System 化している部分以外は、基本的に RDB の仕組みの延長線上で動きます。 しかしフロントエンド上では、使い方を多少考えないといけません。 たいていの B+tree Index の探索は1〜3つのブロックにまず収まり、1〜5回の fetch でレコードの位置がわかります。 ただレコードの中身は様々なブロックに点在していることが多いため、 毎回 fetch しないといけません。これは帯域負荷がそれなりに掛かります。 つまり正規化された DB ではフロントエンド上で範囲検索に弱いことが問題になってきます。

みんな大好き RDB の正規化は、レコードへのアクセスがほどほどに早いことを前提にした概念です。 ネットワーク上やフロントエンド上ではバッドノウハウです。 解決方法は簡単で、教科書通りの正規化を捨てて、 事前に利用用途に応じたレコードの最適化をすれば良いだけです。 もっとわかりやすく言えば、KVS っぽく使いましょうということです。 たとえば範囲検索は結果の配列を JSON.stringify して、一つのレコードに放り込めば良いです。 レコードを一つにすれば一回の Accept-Ranges fetch でデータを取得できるので、大量の fetch を発行せず効率的にデータ取得ができます。 まあレコードはカンマ区切りでも何でも良いんですが、色々なデータを突っ込むことを想定して、JSON でいいかな。 ちなみに数値配列だと効率が悪すぎるので、Blob にすべきと思います。 一般性のあるカラムにしたいなら msgpack を使うのも良さそうです。

似たようなシステムは頑張れば自作もできなくはないです。 ただ sql.js-httpvfs で作れば、仕組みは雑でも完成度 8割のものができます。 何も考えなくてもバイナリでレコード跨ぎが簡単にできるところが強い。 KVS 的に一度に複数レコードのデータを取得する DB に無理やり変えれば、数回の fetch で範囲検索できる検索システムが雑に完成します。 ネットワーク上でも B+ Tree の効率はかなり良いです。 B+Tree の検索コストは log N / log m (m=leaf nodes) なのですが、log m の部分がネットワーク上では強い。 1000万データ 1GB くらいまでは耐えられそうです。 範囲検索も DB の作り直しを惜しまなければどうにでもなりそう。

DB の進化に期待

sql.js-httpvfs で遊んでみると、 DB はもうバックエンドだけの技術とは呼べない状態です。 バックエンドとフロントエンドの区分けとして重要なのは、 I/O の頻度や、セキュリティ、public/private の概念と言えます。 ほとんど静的なオープンデータはフロントエンド上に置いても良いのではないでしょうか。

ちなみに read/write が混在するなら、Cloudflare D1、PlanetScale、FaunaDB、Supabase、Upstash のようなサーバーレス DB、 もしくは Firebase や VPS のようなサービスを利用するしかないと思います。 オンラインストレージ、write が時系列順にしか発生しないなら静的なレコードに退避とか、多少の抜け道もありますけどね

個人的には最初に述べたように KVS っぽいもっとシンプルな実装が欲しいと思っていますが、時間は掛かる気がします。 雑に色々と考えてみたのですが、コンパクトにしようとすると Minimal Perfect Hash とか Succinct Trie (ただし本当に使いたいのは Trie Map) なども考える必要がありそうです。 でもあまり改善はしなそうかな。また色々頑張っても、データ量が増えると結局は最初のハッシュ関数のデータが結構大きくなり、それがフロントエンドではネックになります。 最初の重いハッシュ関数をレイヤーにして振り分けることも考えました。 1億データ以上なら結構差は出そうですが、それ以下だと fetch 回数が 1回減るくらいに思えて、速度差は微妙です。 初期ロードが早くなって 150ms 早くなるくらい? そして 1億データなんてのは、フロントエンド上だと使わない気がする。

やはりつよつよな人が作ってくれた実装を使いたいです。 10年前から実装面では実物がなかなか出てこない世界なので、当分は実装があることが大切で、常に安心感のある SQLite が使われる気がします。

連想ゲーム Rensole の開発メモ

単語の類似度を使った連想ゲーム Rensole (下) を作って、Zenn に紹介記事を書いてみました。 このページではもう少し真面目に開発のメモをまとめておきます。



Rensole では単語の類似度を頼りに隠された単語を探します。多言語対応が簡単なので、 英語日本語中国語 を作っています。 本当はひらがな版も作ろうとしたのですが、予測候補がひらがなだとわかりにくくて微妙だったので、漢字ありです。

Magnitude ですぐに作れるのがウリ

Rensole では 2つの基本語彙のベクトルを比較して類似度測定をしながら単語を探します。 実のところ fastText を Magnitude に変換すれば、すぐにできます。 Magnitude の中身は SQLite DB なので、そのまま sql.js-httpvfs に乗せるだけで実現できます。 基本語彙は約 5万語なので、25億回ほど類似度を事前計算すればもう少しコンパクトに実現もできました。 ただ何かに応用しようとしたときに作り直すのも微妙なので、Magnitude の DB をそのまま使っています。 ゲーム自体は fastText の単語ベクトルと基本語彙さえわかれば、どんな言語にも適用できます。

ゲームにするのは意外と難しい

作ってみると気付くことも多く、Wordle ではアルファベットのヒントを与えやすいですが、 Rensole では与えにくいことが問題になりました。 しかし何らかのヒントを与えないとあまりにも難しくて面白くないので、その解決に苦労しました。

まずはノーヒントで単語を予想するものをモックで作ったのですが、100手くらい普通に掛かることもあって面倒くさかったです。 類語のヒントを出せばそれなりに予想ができるようになったものの、意外と正答率が安定しません。 一瞬だけ Wordle 的なアルファベットのヒントを与えることを考えましたが、それじゃあ連想ゲーム感があまりなさそうだし。 類語のヒント量を 10 から 20 に増やしてもほとんどヒントになっていません。 類語として使えるのは 10 が限界に見えます。 さてどうしたものかとなりました。

そこで日本語版では、文字数→ひらがな/カタカナ/漢字→1文字だけヒント→履修学年のヒントを与えるように実験してみたら、十分解けてまあまあ良かったです。 1文字のヒントはあまりにも大きいのですが、ひらがな/カタカナ/漢字のヒントは 0.5文字くらいのヒントになっていることがうまく働いていると気付きました。 そしてそのヒントは、部分的な読み情報であること、どんな言語にも共通して利用できることに気付きました。

読み情報をヒントにしたら意外と面白かった

海外版は日本語版での実験を踏まえて作りました。 中国語は、文字数→ピンインからヒント→1文字だけヒント→ピンインから1文字ヒント…にしてみました。 中国には漢字配当表がないのでヒントもシンプルです。 日本語版と情報量はほぼ一緒なので、中国語はたぶん解けると思います。 ただ難易度調整はできてないので、解けなかったらごめんね。

英語は日本語や英語より文字数が多いことに注目します。 文字数→発音記号を1つヒント→1文字だけヒント→発音記号を1つヒント→もう1文字ヒントの順にしました。 発音記号は使いやすいライブラリがなかったので、cmudict-ipa という発音辞書を作りました。 発音そのものには CMUDict という素晴らしい辞書があるのですが、特殊な発音記号を使っているので一般的な IPA 形式に変換しました。 この辞書を使って、a-z 以外のアルファベットに対してヒントを出せば、知っている単語なら解けるとわかりました。 一番悩んだのは発音のヒント量です。特に英語が難しい。 文字数に応じてもう少しヒントを出したほうが良いかな? 私だと難しい単語はヒントがあってもなくても解けないので、どれくらいが良いのか何とも言えない。 要望があったら調整してみます。

英語や中国語でも発音のヒントを作ると、今度は日本語も同じようにローマ字のヒントを出しても良いかもと思い始めました。 日本語は中国語や英語と違って文字数が一意に定まらない欠点があり、ヒントになっていないケースも多いですが、 ひらがな/カタカナのヒントにはなりそうです。という訳でローマ字のヒントも付けました。 基本的にはサクサク解けるゲームにしたかったので、ヒントはいくらでも出すノリで作っています。

レベルを分けて誰でも遊べるようにした

ヒントの出し方が決まってゲームの方向性が決まったところで、細かな調整に入りました。 Wordle は基本的に解けるゲームですが、Rensole は難しい問題がたまにあります。 日本語の感触では、だいたいは解けるレベルに収まっていますが、解けない問題は絶望的に解けないです。 こればかりはどうしようもない。 1日1回の縛りは難しいので、すばやく諦めてもらう代わりに、気軽に答えを見られるようにしました。

何十回かプレイしてみると、日本語以外で難易度を上げると、ヒントをいくら出しても太刀打ちできない問題を感じました。 語彙数が増えすぎると、そもそも単語がわからないので、つまらない。 そしてサクサク解けると、毎回わからないことが増えるので、ゲーム性での粗が目立ちました。 ちなみに Wordle も知らない単語が出てくると、最後はブルートフォースアタックになるので同じ課題はあるのですが、 単語の意味がわからなくても解けるし、1日1回縛りだから粗が目立たないようになっています。

Rensole ではこの問題がさすがに気になったので、色々なレベルを用意して単語学習アプリとしても遊べるようにしました。 これで語彙数が少ないからプレイできない、面倒だからいいや、とはならないようにしたつもり。 ただ語彙数が少ないとヒントが雑になるので、簡単になっているかはわからない。

言語資源としても面白い

言語ごとに辞書のサイズと検索結果を比較してみると、割と面白いです。 中国語や英語の語彙数は思っているより少なくて、正規化すると 5万くらいしかありません。 一方の日本語は、正規化しても 8万語はあります。 ちなみに正規化も色々あって、表記ゆれを許容すると 8万語で、表記ゆれを許さないと 6万語になって、どの言語も同じくらいになります。 そういえば 日本人の語彙数は小6で 2万語、大人で 5万語 とわかっています。 これは正規化していない状態を指しているんじゃないかな。 英語のネイティブの語彙数は、色々なサイトを見ても 3万くらいしかないんですよね。

語彙数の違いは、少ない語彙数でのゲームに影響が大きいです。 英語は 1000語くらいでもゲームになりますが、日本語は 5000語くらい用意しないと、ゲームになりにくい感じです。 上位に助詞や感嘆詞が大量に入ってくるのが一番の原因に見えますが、表記ゆれが頻度の高い語彙でも重要ということでしょう。 たとえば「歩く」すら上位 1000語に入らないのは、なかなかすごいなと思いました。

最後に

何となく形にはなったのですが、これで良いのかどうかは怪しいところが多々あります。 プレイする人がいたら、その声を聞いて改善できたら良いなと思います。 作ってみた気付きとしては、言語系のゲームは最初から日中英も作ればグローバル展開できるかわかるので、一番最初にやるのが良さそうです。

2022年5月20日金曜日

fastText で類語辞書を作った

fastText で類語辞書を作りました。 やってることは fastText から基本語彙だけに着目した類語をリスト化して、 SQLite で高速に単語検索をできるようにしただけです。

何に使うか

何のために作ったかというと、(1) fastText の予測が遅いので毎回演算せずに使いたかった。 (2) fastText の予測結果が使いにくいので、使いやすくするとどうなるのか知りたかった。 そして (3) Web上で使うためです。

(1) fastText や Word2Vec がどんな類語を返すのかか、ふと思い出したくなりました。 ただ確認するために色々やることがあって面倒です。 モデルのダウンロードで 10分、Magnitude に変換で 10分、処理に 1分みたいな感じでかなり時間が掛かるので、 Web 上で、3秒で類語を表示できると嬉しいなと思いました。 そのためにはベクトル演算をせず事前計算してすぐに表示でるようにすればいいだけです。 類語の関係はほとんど静的なので、わざわざ演算する必要性はありません。

(2) Word2Vec や fastText の様々なモデルで遊んでいると、どれもまったく予測結果が異なることに気付きます。 この原因は、単語の扱い方も、前処理も、学習した対象物も、まったく異なるからです。 つまり Word2Vec も fastText の pretrained model はそのまま使っても、あまり使い物にはなりません。 まず最初に利用用途に応じて不要な単語を削除して調整する必要があります。 現実には「類語を知りたい」というニーズがありますが、すぐ使える状態まで調整されたものはありません。 調整するとどのような情報が得られるのだろうと思いました。

構築に当たって最低限考えるべきこと

fastText は未知語も処理できますが、昔遊んでみたときもあまり予測精度は良くなかった記憶があります。 久々に遊んでみると、活用形のようなものが大量に得られて、うーん…という感じ。 活用形を得たいなら形態素解析辞書を使えば良いです。 複合語っぽいものも得られやすいですが、これも SudachiDict の B基準でマッチングすれば済みそうに見えます。 また Word2Vec も min count が下がると顕著に予測精度が下がることは以前から感じていました。 さらに、モデルの結果がバラバラになりがちな問題もあります。

結果がバラバラということは、不安定な語彙が多い (特に固有名詞) ということなので、語彙を絞って結果を安定させるのが良いと思いました。 すぐに使えそうなのは基本語彙に限定した辞書です。 基本語彙に絞れば、ベクトルに大きな揺らぎがあるとは思えないので、使い物になりそうです。 これまで英語や日本語の活用形を正規化した単語リストを作ってきたので、 それらを利用すれば良い類語辞書ができそうと思って作ってみたところ、まあまあの出来でした。

日本語の難しさ

辞書を作ってみると、日本語はやはり他の言語より扱いが難しそうです。 (1) まずは表記ゆれ。「たい」は「鯛」なのか「隊」なのか文脈がないとわからないです。 最初は SudachiDict の正規化を利用すると良いかと思ったのですが、普通名詞と固有名詞の扱いが微妙に怪しい印象を受けました。 たとえば「次郎」や「清心」は、正規化された状態だと普通名詞として扱われるのですが、 正規化前は固有名詞になっていたりして、一体どう解釈すれば良いのだろうと思いました。 他にも「きゅうり」は「胡瓜」に正規化されますが、常用漢字の水準を超えてしまうと、類語としての予想がしにくくなるように感じます。 こういうのはいざ作ってみないとわからないものです。

(2) 動詞の名詞化。これは応用を考えた時に問題になります。 たとえば「乗せ」とか「回し」は連想で浮かばないし、「思う」と「思い」や「多く」と「多い」の違いは無視したいケースがありそうです。 でも「祭り」と「祭る」は区分けしたいという気持ちもあって、かなり扱いが難しい。 どうすればいいかが一番わからんとこですね。

個人的な結論だけ書くなら、類語辞書は以前作った graded-vocab-ja と同じルールで作り、表記ゆれだけは許容するのが良さそう。 応用する時は、普通名詞と・動詞・形容詞に限定し、最後にひらがなが付く名詞は数も多くないので丸ごと捨てて利用が一番良さそう、となります。

基本語彙さえわかれば簡単に作れる

fastText や Word2Vec のリストを示す言葉はないように見えます。 Synonym は含んでいるけど、確実ではないからなあ…。 日本語だと類語という言葉がまあまあ近いと思ったのですが、新たに名前を付けたほうが良さそうな気がしました。 similarity から siminym という造語を作って表現し、 Siminym-ja, Siminym-en, Siminym-zh という名前でライブラリ化 / サイト化しました。

頻度リストや形態素解析辞書などの言語資源の整っている日本語・英語・中国語だけ作りました。 fastText は非常に多くの言語をサポートしているので、あとは基本語彙さえわかれば他の言語も作れます。

実際に応用してみると興味深い

公開に当たっては事前に応用アプリまで作っているので、出来はかなり確認しています。 一番気になったのは、語彙数が増えたときに非ネイティブの言語では、知らない単語が多すぎることでした。 語彙数が少ないと物事の見え方が違って使いにくいことを実感したので、語彙数を調整した類語辞書を同時に表示するようにしてみました。 小さな子や非ネイティブの人がどのように言葉を捉えているかを、割とうまく可視化できているんじゃないでしょうか。 うまく使えば色々なことに使えると思います。

まあまあの出来なので公開してみましたが、逆にまあまあでしかないことも確かです。 類似の概念がないときには微妙な結果が羅列されますが、これはどうしようもありません。 類似度を見れば多少は除外できるのですが、それだけでは除外しきれないものも多々あります。 そういったズレも目を潰れるケースなら使い物になりそうかなあ。 作ってみると色々思うところもあったので、そのうち似たようなものを作る予定です。

2022年5月9日月曜日

AA を表示の崩れない SVG に変換する

ハゲ打 のアイコンを更新するために、AA を表示の崩れない SVG に変換する必要がありました。 今時そんなことをやっているのは私くらいかも知れませんが、たくさんの文字を SVG 化するニーズ自体は割とあります。 そんな訳でメモを残しておきます。

1. AA をフォント埋め込み SVG にする

まずは Ascii Figure To Svg を使って、 AA を SVG 化します。これで話が終われば良いのですが、実際にはここからが問題だったりします。 出力される SVG はフォントを外部からロードする形式で記載されています。 MS Gothic は Linux にないので、このままでは表示が崩れてしまいます。 そこでフォントを SVG のパスに置き換える作業が必要になります。

2. フォント埋め込み SVG をパスだけの SVG にする

1 で作成した SVG を Inkscape で開き、一つ一つの文字をパスに変換していきます。 文字を選択した後に、メニュー→パス→オブジェクトをパスへ、をクリックすれば文字をパスに変換できます。 この機能は超便利だけど、毎回存在を忘れてしまうのが玉にキズです。 なんか CLI ないのかな。

3. 見た目の細かな微調整をする

フォント埋め込みの SVG をそのまま転用しようとすると、文字の太さなどが足りなかったりして、微妙に調整をしたいことがよくあります。 文字そのままだと編集できませんが、パスに変換できればこちらのものです。 パスのストロークを変えれば、太さや丸さを調整できます。 ただのパスなのでスケールも簡単に変えられ、割と調整が効きます。

新しくなったアイコンがこちら。前よりスリムになってしまったけど、良い感じ。 ストロークを太くした後に丸くして、縦方向に 1.2倍伸ばしています。



2022年5月1日日曜日

Emoji Fill Hole を作った

Emoji Fill Hole を作りました。 絵文字に対応するアルファベットを穴埋めする英単語アプリです。 日本語版もあるので小さな子の語彙学習ゲームとしても使えます。



小さな子がよくやる穴埋め問題を作るとき、それっぽい絵を付けるのが一番大変なので、まずは絵が付いているのが良いところです。 絵が付いていると解答が一意に定まりやすくなるのも利点です。 また一文字を書くだけで良いので、英単語の羅列を全部覚えきれない子に適しています。 飽きやすい小さな子 (〜小2くらい) を対象とした、最初の英語に良いかも知れません。 小3くらいになると、さすがにもう少し難しいほうが良いかな。

2022年4月26日火曜日

SpeechSynthesis のテスト用に TTS Demo を作った

Web ブラウザで Text-to-speech の簡易的なテストをしたい時が結構あります。 しかし巷で公開されている Text-to-speech のデモはだいたいバグっているので、うまくテストできません。 毎回自分でデモを作って確認せざるを得ず、地味に手間となっていました。 そこでまともな SpeechSynthesis のデモとして、TTS Demo を公開することにしました。



声はあえて手動で選択せずランダムに選択するようにしたのですが、 これは英語のテキストを日本語で Text-to-speech したい時などに、 声を選ぶのが大変なように思えたからです。 完全に自分用に作ったのですが、割と便利だと思います。

2022年4月22日金曜日

Ubuntu デスクトップ比較: 22.04 も Lubuntu で決まり

Ubuntu 22.04 がリリースされたので、いつも通り Ubuntuデスクトップ比較をしてみました。 比較条件は 20.04 のときと一緒で、以下。
  • VirtualBox でクリーン環境を比較
  • 検証時の最大メモリサイズは 2GB
  • 起動→安定後のメモリ使用量を free --mega で比較

結果はこちら。
  • Ubuntu: 738MB → 712〜727MB (最重量)
  • Lubuntu: 316MB → 351〜355MB (最軽量)
  • Xubuntu: 415MB → 467〜483MB
  • Kubuntu: 409MB → 489〜537MB
  • Ubuntu MATE: 523MB → 507〜534MB
  • Ubuntu Budgie: 632MB → 674〜684MB

素の Ubuntu と MATE がすこし軽くなった一方で、その他はメモリ使用量が増えました。 メモリ使用量が増えた理由はわかりませんが、 Lubuntu 以外はフレーバー間のメモリ使用量の差はほとんどなくなっています。 1回測っただけだと少し怪しい感じだったので 2回測定してみましたが、 Xubuntu が少し軽いかなというくらいです。 どのフレーバーも頑張った結果とも言えそうですが、Lubuntu 以外は UI で選べば良いような気がします。

メニューを表示したときのスクリーンショットをまとめると、こんな感じ。 どれも良い感じに思うけど、Lubuntu かっこいい。

Ubuntu
Lubuntu
Kubuntu
Xubuntu
Ubuntu MATE
Ubuntu Budgie


しょぼマシンをたくさん使ってる身としては、私は今後も Lubuntu で決まりです。 Xubuntu と Kubuntu のメモリ使用量がだいぶ増えたので、メモリ使用量を削減したくても Lubuntu が動かないような場合、 他のディストリビューションを検討してみても良いのかも知れません。

Web N-gram を用いた英語の共起コーパス wncc-en を作った

Web N-gram を用いた英語の共起コーパス wncc-en を作りました。 名前は Web N-gram based Collocation Corpus の略です。 検証用に作ったものなのですが、まあまあの出来には見えます。 日本語版がうまくできたのでノリで作りましたが、日本語ほど使いやすくないかな?



英語では Google N-gram を使って共起関係を抽出します。 Google N-gram は大きすぎるので、事前に軽量な N-gram データ (google-ngram-small-en) を作って、そちらから作っています。 Google N-gram には 単語_品詞 の形で品詞情報が付いているので色々と活用もしやすいです。 ちなみに 4gram はデータ量がヤバイので解析していません。

品詞情報は DB を生成してみたところ微妙だったので無視しました。 他にも過去形を正規化するべきかどうか迷いましたが、日本語より正規化が面倒なので、正規化せず処理することにしました。 少しデータサイズが大きくなりますが、気にするほどではないかも。

抽出できると嬉しそうな基本ルールは以下ですが、英語は割と雑に順序を入れ替えても何とかなります。 実のところ何も考えずにすべての N-gram を登録してみました。

2gram

  • S [verb]
  • [verb] [adverb]
  • [adverb] [verb]
  • [adjective] [adverb]
  • [adverb] [adjective]
  • [noun] [noun]

3gram

  • S [verb] [adverb]
  • S be [adjective]
  • S be [noun]

今回作ったデータをもとに、また何か作れそうです。 ただ最初に書いたように、あまり使い勝手は良くなさそう。 サジェストにでも使えるかな、というくらい。

2022年4月15日金曜日

Web N-gram を用いた日本語の共起コーパス wncc-ja を作った

Web N-gram を用いた日本語の共起コーパス wncc-ja を作りました。 名前は Web N-gram based Collocation Corpus の略です。 精度と網羅性の高い例文や用例、共起データが欲しかったので開発しました。 検証用に作ったのですが、まあまあの出来には見えます。



共起辞書は昔 NICT がやってたみたいですが今は存在不明、 例文や用例データとしてすぐに使えるコーパスとして WordNet、 単語ベクトルを算出する Word2Vec がありますが、後述するように難点はあります。 他にも例文や用例は検索サイトならいくつかあるのですが、 再利用可能なデータがなく精度や実用性にも少し疑問があります。

既存の例文生成の問題

例文や用例は課題がわかりやすいので取り上げてみます。 生成手段としては青空文庫や Project Gutenberg から全文検索で作る方法が有名です。 しかし (1) 文章が長すぎ、(2) 文調が古すぎます。 上記の問題を解決するには頻度を考慮した適切な文章を生成する必要があります。 そのためには言葉の意味、最低でも共起関係をきちんと見ることが重要となります。 例文や用例は、文章が長くなっても言葉のイメージが掴めない問題があるので、 語彙の意味や利用シーンがわかることが重要となります。 一番わかりやすい例は「真理の追究」と「責任の追及」です。 同音異義語の「ついきゅう」はたった一つの周辺語さえ提示すれば、 利用用途が完璧にわかります。これは地味に凄いことです。

Word2Vec や WordNet の問題

共起コーパスとは異なりますが、語彙の関係を抽出する試みとして有名なものに Word2Vec があります。 Word2Vec は網羅性が非常に高いのが利点ですが、残念なことに考慮しているのは類似度です。 類似度と共起は近いようで異なるため利用が難しく、例文や用例の生成もできません。 先ほど例示した「追究」と「追及」を例に挙げると、Word2Vec では非常によく似た語として扱われてしまい、 類似の候補としてそれぞれが提示されてしまったりします。 Word2Vec の情報をもとに例文を作ることは厳しいなと思いました。

言葉の出現分布の類似度ベクトルは、必ずしも類義語を示す訳ではありません。 fastText も同じことが言えて、サブワードの出現分布の類似ベクトルが似通っていても、類義語を指すとは限りません。 同じような文脈で使われることが多いことを示すだけです。 結構色々と遊んでみましたが、例文や用例として応用するのはやや難しいという印象です。

例文や用例、共起データをすべて含む WordNet は一番使い勝手が良く、一応は使い物になります。 ただややノイズが強く、概念が冗長過ぎる割に、単語の網羅性が低く、 例文や用例もやや冗長な欠点があります。 いざ使おうとすると様々な問題にぶつかり、仕方なく自作したという事情があります。

作ってみた

それではどのように共起関係を抽出すればいいでしょうか。 共起関係を抽出するには、日本語の助詞をよく見て述語構造解析に近いことをやれば良いだけです。 解析は真面目にやると死ぬので、自明な関係だけを処理します。 日本語も英語も考え方はほとんど一緒なのですが、実装はなかなか面倒です。 話が長くなるだけなので、抽出ルールは以下のページに分けて記載しました。

GPT2 や BERT との比較

N-gram で重要な関係だけ抽出する考え方自体は、GPT2 や BERT の穴埋め問題の考え方に近いところがあります。 ただ長文と短文のバランスを考えたりする必要があって、GPT2 や BERT の計算ステップは無駄が多い。 良い短文は数 gram を頻度カウントすれば生成できる直感があるので、わざわざ GPT2 や BERT を使う必要はない気がしています。 ちなみに GPT2 による例文生成もやってはみました。 なんとなく生成はできるのですが、結果が不安定だし、問題によってはボロボロになりがちなで、実用には耐えませんでした。 わざわざ問題を難しくする必要はないのかなと思います。 BERT もダメダメで使い物になりませんが、GPT2 よりは良いかな。 ただ語彙数が少ないため、それに伴う悪影響が大きかったです。

欠点

欠点もあります。(1) 名詞、動詞、副詞、形容詞に付随する関係性を抽出するのは簡単ですが、接続詞は難しいです。 少し考えてみても、接続詞でいい感じの関係性を抽出するには多くの ngram を必要とします。 もっとも日本語の接続詞はそれほど多くないので、接続詞は別途処理すれば、十分に使い物になるコーパスだと思います。

(2) 関係性を抽出できると言っても、あくまで頻度の多さでしか判断できません。 例えば、「責任を追求する」「顔が火照る」で用例はわかるかも知れませんが、意味は正確にわかりません。 ただ日本語は漢字の並びでだいたいの意味がわかるので、案外どうにかなるのかも知れません。 例えば適当に「激燃る」という動詞を作ったとしても、なんか「すごい燃えるんだな」と、十分に意味が通じます。 「しばしば (屡々)」みたいな現代の知識ではどうにもならないケースだけ対処すればいいはずです。

まとめ

とりあえず生成データは既存手法よりずっと使いやすいです。 抽出手法もわかりやすく、せいぜい 5gram くらいの形態素 Web N-gram と、最低限の文法知識だけ用意すれば、汎用的に多言語対応できるのも利点です。 データセットは使いやすい形で公開しているので、使い方はどうぞ。

日本語の共起コーパス wncc-ja の抽出ルール

日本語の共起コーパス wncc-ja を作りました。 日本語と英語を作っているので、概要については こちら を参照してください。 このページでは日本語の抽出方法についてだけ、まとめています。

さて日本語を解析するためには文法について考えないといけません。 日本語の文法は形態素解析の仕様にまとまっているので、以下の 2ページを読んでみてください。 特に重要なのは「助詞」です。
wncc-ja では 日本語ウェブコーパス 2010 の形態素 N-gram を利用して、 [名詞] に対する共起関係を抽出しています。具体的に利用している抽出ルールは以下の通りです。

前方共起

2gram

  • 形容詞 [名詞]
  • 連体詞 [名詞]
  • 動詞(自立) [名詞]

3gram

  • 形容詞(タ接続) た [名詞]
  • 動詞(自立・タ接続) た [名詞]
  • 動詞(自立) 動詞(非自立) [名詞]
  • サ変接続名詞 動詞する [名詞]

4gram

  • 動詞(自立) 動詞(非自立・タ接続) た [名詞]
  • サ変接続名詞 動詞する た [名詞]

後方共起

3gram

  • [名詞] が(格助詞) 形容詞
  • [名詞] は/も/こそ/でも/しか/さえ(係助詞) 形容詞
  • [名詞] など/なり/やら/か(副助詞) 名詞
  • [名詞] (並列助詞) 名詞
  • [名詞] は/も/こそ/でも/しか/さえ(係助詞) 動詞
  • [名詞] ばかり/まで/だけ/ほど/くらい(副助詞) 動詞
  • [名詞] を/へ/と/から/より/で(格助詞) 動詞

4gram (動詞は 1gram 走る or 2gram 追及|する)

  • [名詞] は/も/こそ/でも/しか/さえ(係助詞) サ変接続名詞 動詞する
  • [名詞] ばかり/まで/だけ/ほど/くらい(副助詞) サ変接続名詞 動詞する
  • [名詞] ばかり/まで/だけ/ほど/くらい(副助詞) 動詞 動詞
  • [名詞] を/へ/と/から/より/で(格助詞) サ変接続名詞 動詞する

3-5gram (不採用)

  • [名詞] が/に(格助詞) 動詞 助動詞
具体例:
  • 猫|が|走る
  • 水|が|飲み|たい
  • ダム|が|破壊|さ|れる
  • 前|に|向かう
  • 風|に|吹か|れる
  • 兄|に|追求|さ|れる

正規化

基本語彙のみ処理するときは、せいぜい 4gram を見ればいいのでかなりシンプルと思います。 助動詞は面倒なので辞めました。 とはいえサクッと考えたルールなので、完璧ではないかも知れません。 ちなみに少し迷ったところは 4つあります。

(1) まずは動詞や形容詞の非自立です。 「走り続ける猫」の「続ける」はあまり意味がない情報です。 ただ非自立かどうかを判定するには gram 数が結構必要なので面倒です。 もう形容詞/連体詞なら 2gram、動詞なら 2-3gram で良い気がしました。

(2) 助詞に関しては、形態素解析器の結果をそのまま使えるかはきちんと検討してないですが、 不採用にした「が」と「に」は難しそうな気がしたのでハードコーディングにしました。

(3) 「赤かった花」のように日本語では高確率に「た」が出現するのですが、 こういった活用形のルールをどこまで真面目に考えるべきか。 無視しても良かったのですが、すべて「赤い花」に正規化してみました。

他にも未然形を除外したり、体現接続特殊を除外したり、数詞を除害したり、細かな解析ルールはあります。 あと gram 縛りをしていると気になるのは「〜性、〜感、〜事、〜度」のような接尾辞です。 接尾辞は 3gramだと名詞と判定されてしまうけど、4gramだと接尾や非自立になるので多少のノイズが発生します。 そのへんはコードを見ればわかるのであまり深くは書きませんでした。 細かいところはデータベースを作って、文法ルールのノイズや漏れを確認するのを 10回くらいくり返して、ルールを決めました。

ルール設定は可能な限り努力してみましたが、もっと良い実装があれば今後も改善はしてみたいと思ってます。

2022年4月9日土曜日

Google Ngram (English) の small 版を作った

Google Ngram (English) の small 版 を作りました。 Google Ngram はとても便利なものですが、いかんせんサイズが大きすぎて個人には使いにくい代物です。

google-ngram-small-en

そこで、英語に限定、時間軸は latest、閾値 > 999 で小さくして 1GB 以内に収めたデータセットを作りました。 ほとんどのユースケースはこの条件で足りると思います。 名前は google-ngram-small-en としました。 案の定データ量がやばかったので、構築には一ヶ月くらい掛かりました。 並列実行すればもっと早くできたと思いますが、あまり Google に負荷掛けるのも嫌なので、まったり作りました。 3gram でもデータ量がキツイので、4gram は作ってません。

英語を使って何かしようとすると、なんやかんやこのデータセットを作らないといけないケースが多々ありました。 割と使えるデータセットになっているんじゃないかな。

2022年4月2日土曜日

英会話アプリ Gratalk を作った

日本の英文法学習に従いながら英会話練習できるアプリ、Gratalk を作りました。 ちなみに Speecha という英会話アプリも以前作っていますが、そちらは平文です。



SpeechaGrament で基盤はすべて作ってあるので一瞬で作れたのですが、 ネーミングセンスがないので相変わらず名前が難しかったです。 悩んだ挙げ句、grammar と talk を混ぜて、Gratalk としました。 以下のように S と G を揃えた命名規則にすることで、平文と英文法のアプリ群を見分けやすくしました。
  • Sentency ↔ Grament
  • Speecha ↔ Gratalk

軽く英会話練習をしてみて思ったのですが、学校で習うテンプレ英語ってやっぱ楽ですね。 Speecha の小3〜小4 モードと Gratalk の難易度はレベル的に大差ないはずですが、 Speecha のほうがかなり難しく感じます。

Gratalk の公開にあたって、Speecha もかなり改良を加えておきました。 これまでは既存の音声認識を使っているせいで、固有名詞の判定が厳しい問題がありました。 これはおそらく固有名詞の出現頻度が低く十分な訓練が行われなかったり、日本語の英語表記が本質的に発音しにくいことによると思っています。 そこで固有名詞っぽい単語が出てきたら未知語として処理し、EASY モードでは採点から外すようにしました。 これによってかなり利用しやすくなったと思います。

2022年3月15日火曜日

Linux の Firefox で Text-to-Speech ができるようになっていた

Linux の Firefox で Text-to-Speech ができるようになっていました。 Windows / Mac だと Microsoft / Apple の手を借りて Text-to-speech ができたのですが、 Linux だとずっと方法がわかりませんでした。

ふと見つけた speech-dispatcher のキーワード周りを調べてみたら普通に方法が載っていました。 忘れると困るのでメモしておきます。
一番上の内容はすこし古いので、いまは以下のコマンドを打つだけで、Text-to-Speech ができます。 なお発話精度は酷すぎて何言っているかわからないですが…。
sudo apt install speech-dispatcher
ボタン一つで動くようになったという事実が大きいです。

あとは発話精度の良い話者モデルを簡単に利用できるようになれば良いだけです。 たとえば festival をインストールすればギリギリ許せるくらいにはなります。
sudo apt install festival speech-dispatcher-festival
参考文献には設定が必要と書いてありましたが、Lubuntu 20.04 & Firefox 98 では特に何も設定は必要ありませんでした。 再起動すれば終わり。 いつからできたのかわかりませんが、ついにほとんどのブラウザで Text-to-Speech のサポートが完了しましたね。

2022年3月14日月曜日

ttf2svg を作った

@marmooo/ttf2svg を作りました。

ttf2svg

機能は名前の通りで、qdsang/ttf2svg という同名のツールがあるのですが、 これは TTF を SVG フォントに変換するもので、1つの文字を SVG として切り出すようなことができません。 本当に欲しかったのは、その部分です。 後からパースすればできないこともないんですが、手軽さが必要なので…。

例えばどんな時に使うかというと、アイコンなどを作るとき、1文字だけ埋め込みたい時があります。 SVG にフォントを埋め込むと環境によって表示がズレてしまいます。 ズレないような表示が必要な場合、ライセンスをよく見ながら SVG として切り出して、 利用させてもらうほうが合理的だと思っています。

ttf2svg 以外の名前を考えつかなかったので、scoped packages で公開しました。

2022年3月12日土曜日

タッチde書き順を作った

タッチde書き順 という漢字の書き順ゲームを作りました。 書き順だけのアプリは色々ありそうですが、書き順の理解チェックもでき、 1学年ぶん一気に学習できるゲームっぽいアプリにしました。



漢字は得意な子と苦手な子で学び方も違うので、 手間を省かせてあげられるような仕組みを用意したいものです。 例えば得意な子にドリル教材は必要ないので、もっと有意義に時間を使ったほうが良いでしょう。 どちらの子もテストができれば良いはずで、 そこまでの過程にはかなりの無駄があるように思います。

今回作った タッチde書き順 は、 その壁を取り払うために、漢字を知って、書き順を覚えるという作業をすべてゲームにしました。 割と色々な使い方ができそうです。 まずは予習で漢字をさらっとすべて学ぶのに良いでしょう。 もともと漢字を知っている子は 15分くらいで1学年の学習が終わります。 それで覚えられれば漢字テストで良し、足りなければ タッチ漢字ドリル を練習をする。

苦手な子が一気に復習するのにも良さそうです。 書き順は完璧に覚える必要はないと思っていますが、 苦手な子ほど書き順が崩壊していることが多いので、高速復習に使えます。 綺麗さを強く意識せずにゲーム感覚でできるようにしたので、 文字が汚い子も遊びやすいと思います。 計算ゲームみたいな感覚でプレイできるので、割とおすすめかも。

「廴」のように画数が変わる面倒くさい漢字も若干ある訳ですが、 その判定はかなり難しいので、学習はゆるめにしています。 ストレス軽減を重視しました。

2022年3月8日火曜日

transition.css がアニメーションの実装に便利そう

たまたま argyleink/transition.css というアニメーションライブラリを見つけました。 これは便利そうです。 以下のようなアニメーションを、コピペで簡単に作れます (画像は公式より拝借)。



animate.css とよく似たライブラリですが、いくつか利点があります。
  1. アニメーションを自由に作れる
  2. 作ったアニメーションの CSS を部分的にコピーできる
(1) アニメーションの秒数や、開始・終了時のエフェクトを簡単に編集できます。 色々なものに適用しやすそうで、良い特徴だと思いました。

(2) 公式ページ で使いたいアニメーションの CSS だけコピーできます。 animate.css だと何も考えずに使おうとすると、ライブラリ全体を読み込まないといけないので利点です。 まあ animate.css も使うところだけ自分でコピペすれば良いので、慣れている人には関係ないかも知れませんが、楽さは重要です。

欠点としては、まだビルトインのアニメーションの数が少ないことでしょうか。 最近のナウいページで使われがちなアニメーションは網羅されていますが、 ゲームなどで面白い効果を出そうとするには、まだまだ不足している気はします。 animate.css のほうが使えそうなアニメーションが多いです。 ただ、いくらでも増やせる実装になっているので、これから増えていくと面白そうです。

今後に期待ですね。 ちなみに loadingio/transition.css というのもあって、これはこれで便利そう。 ただこちらは導入に少し手間が掛かる。

2022年3月5日土曜日

英文法タイピングを作った

英文法タイピング を作りました。 Grament で作った文法問題をそのまま使ってるので、作るのは簡単。



これで ABC → フォニックス → 英単語 → 英文法 → 英文、ときれいにカバーできました。 文法はまだ完璧じゃないけど。 派生部分はもう少しゲームを作りたいけど、高校生くらいまでの基礎英語はもう遊んでるだけで学べますね。

2022年3月1日火曜日

英語チートシートを作った

英語チートシート を作りました。 すこし前に作った英文法の学習アプリ Grament で遊ぶときに、簡易的な文法サイトが欲しいと思ったからです。



いざ作り始めるとなかなか大変だったので、ひとまず中1の範囲までを公開することにしました。 まあ中1英語まで完璧にマスターすれば、何となく英語が読める状態にはなるでしょう。 そこから先は Sentency でも十分学習できるとは思います。 ただ、当然ながらその先についても追加したいとは思っているので、残りはじっくり作っていきます。

英語チートシート の副産物として、不規則動詞の暗記シート も作りました。 不規則動詞の暗記シートって、みんな適当に自作したりしていそうですが、いい感じに使い回せるものが欲しいですよね。 今回作ったものは部分的にテストしたり、カスタマイズできるようになっています。 どちらも A4 で綺麗に印刷できるようにも調整してあるので、使い勝手は良いんじゃないかと思います。

不規則動詞の暗記シート は微妙にページを跨いでズレがちですが、 ある程度スキルがあれば、Dev Tools でページを開いてちょちょいと編集してください。 それでも足りない時は、html タグに contenteditable 属性を付けて弄ればいくらでも調整できるので、まあ良いかと思っています。

2022年2月26日土曜日

手書きIME のメモ帳を作った

メモ帳として使える 手書き IME を作ってみました。



最初は一筆書き IME を作りたいと思ったのですが、画数が増えると精度が微妙でした。 ひらがな/カタカナはほぼ 100% 認識するので残念。 漢字は学習データを改良しないと思ったとおりに入力できなそうです。 すぐには無理そう。という訳で平凡な手書き IME のメモ帳にしました。

なるべくコンパクトな UI の IME を作りたいと思っていたので、 canvas の大きさを色々と実験しました。 結論としては、画数の多い漢字を小さな canvas で書くのは大変で、90px は必要でした。 iOS では canvas の外枠に触れると誤反応が起きるので、 やや大きめにしたほうが良さそうで 120px くらいが良さそうです。

120px という数値は IME としてはとても小さいです。 たとえばフリック入力や仮想キーボードは 200〜300px くらい専有して邪魔ですよね。 いつか使えるかもなあと思ったので、とりあえずコミットしときました。 まあ Android / iPhone の IME は JavaScript で書き換えられないので、限界はあります。

今はメモ帳として使うくらいが関の山ですが、現状でも割と便利な気はします。

2022年2月23日水曜日

英文法の並び替え学習アプリ Grament を作った

Sentency より日本英語ちっくに、平易な文法を学ぶアプリとして、Grament を作りました。 Sentency はふんわり口語の英文法を理解するのに使いやすいですが、小さな子が音声なしで学んだり、HARD モードで学ぶのはキツイです。 もう少し文法を固めるアプリも欲しいと思いました。 Grament では Sentency と異なり、平易な文法ごとにくり返し学習します。 例文は頑張って作ったので割と大変でしたが、ひとまず中1英語までサポートしました。 漢字はなるべく小学漢字の範囲に収めました。



わかりやすい学習順序で文法を学ぶ

文法を学ぶ順序については、結構色々な参考書を見て考えました。 中レベル向けの参考書って、どれも文法がごった煮で学んでいますね。 唐突に What が出たと思ったら、When はもっと後で学んだり、ぐちゃぐちゃです。 I / You はすぐ学ぶのに、We / They はずっと後で学んだり、次の学年のものが混在していたり、いまいちよくわかりません。 平均以上の子も想定して量を増やしているからと思いますが、そのような構成だと再利用性が低いですね。 もっとわかりやすい構成があると思うし、指導要領の改定を考えると、参考書の並び順はコストが掛かりすぎると思いました。 個人的には英語は基本的なルールを掴めば、覚えるべき文法も練習もほとんどないと思っているので、 文法はあっさり学んであとはひたすら演習のほうが、良いんじゃないか。

低レベル向けの参考書だと綺麗に体系化されていて、私もそちらのほうが絶対わかりやすいと思っているので真似しました。 低レベル向けの参考書では、いきなり SVOC をサラッと学んでいて、とても良いです。 そして人称代名詞をどーんとすべてまとめていて、とてもわかりやすい。 「これだよこれ」という気持ちになりました。 ただ低レベル向けの参考書だけだと足りないものも多いのです。 たとえば受験や英検を想定するなら、練習量が足りませんね。 Grament は低レベル向けの参考書の補完になる実践アプリになると思います。 中2以上はまだできていませんが、中1の英文法さえ押さえてしまえば、Sentency を活用しても良いし、何とかなるでしょう。 残りはじっくり作っていきます。

文法アプリって名前を考えるのが難しいね

どうでもいい話ですが、今回のアプリ名は結構色々と考えました。 Grammar という単語は長い上に代替案がないからです。 Grammarly や Grammy と被ると面倒なので大変でした。 最近は、なんちゃらgram という名前を付けるのが流行っている気がするので、そこを真似しようと考えました。 そもそも programming とか gram ってどういう意味か、私は考えたことがありませんでした。 語源は program → pro [前に/へ] + gram [書かれたもの/作品] らしいです。 文法の grammar は gram[書く] ar [性質] じゃないかな。 ar 以外の性質を示す接頭辞 ent / al / ive / or と gram を繋げた造語の中で、 grament は響きも良いと思ったので、これに決めました。 接尾辞 ment は [こと] の意味もあるので、まあまあわかりやすいかな、と。

2022年2月19日土曜日

drop-inline-css を作った

drop-inline-css というコマンドラインツールを作りました。 HTML ファイルを解析して不要な CSS を削除し、インライン化した HTML を生成するビルドツールです。 設定ゼロの超お手軽ツールです。

drop-inline-css

実のところ、私が真面目なサイトを作る時は、似たようなビルドツールを作って動かしていました。 いい加減なサイトにも同じ仕組みを適用したいと思い初めて、作り直しました。 作り直した理由は、esbuild のように、ツールチェインがだいぶ高速になってきたからです。 まず parcel/css が出てメンテの大変そうだった CSS Minify に決定版感が出てきました。 HTML Parser は中身を操作しようとすると選択肢が一気に減るのですが、 node-html-parser が早そうな上に、使い勝手が良かったので採用しました。 普通は jsdom あたりを使うのかな。 そこにみんな大好き DropCSS を組み合わせます。 現時点で最速/最高性能と思われるライブラリを組み合わせると、割といい感じです。

ボタン一つでサイト全体の Critical Path を最適化

さて drop-inline-css の使いどころですが、シンプルなページの Critical Path の最適化 (above the fold) に使えます。 ちなみにツールでは画面内に収まっているかのチェックなどはしておらず、HTML 全体の外部 CSS をインライン化しているだけです。 そのため簡易的なページにしか使えませんが、個人的にはそれで十分と思っています。 複雑なページなら他のライブラリを使えばよく、シンプルな大量のページをストレスなく最適化するほうが大半のニーズだからです。 また Critical Path を手作業で抽出した後に適用しても良いですよね。 わざわざ Critical Path の自動抽出のような、実行時間が極端に遅くなるような処理を実装する必要はないと思ってます。

drop-inline-css は、たとえば Bootstrap のインライン化には使いやすいです。 Bootstrap の CSS をそのまま読み込むサイト場合、160KB くらいロードしてからレンダリングが始まります。 drop-inline-css で不要な CSS を削除してインライン化すると、ほとんどのサイトは 5〜20KB でレンダリングを始めることができるようになります。 あとから元の Bootstrap を読み込むので 10KB くらい通信量は増えてますが、体感的にも Pagespeed Insights のスコア的にもかなり向上します。

最近は CSS in JS が流行っているので、Critical Path の最適化はやっている人のほうが少ないかな? 個人的には CSS in JS よりこちらのアプローチのほうがずっと楽だと思うんですよねえ。 Node.js や JavaScript Framework、そして Webpack に頼り過ぎると、設定とマニュアルに振り回されがちになり、逆に苦労が増えることのほうが多い気がします。

ローカルビルドでの利用例

drop-inline-css を使えば、何一つ悩むことなく、ボタン一つでサイト全体を高速に最適化できます。 tdewolff/minify と組み合わせると最強に思ってます。

ただ、Vercel / Netlify で使うにはどうするのが良いんだろう。minify コマンドとの組み合わせが難しいです。 ローカルビルドじゃないと苦労するかも。 まあ私はだいぶ前から基本的にはローカルビルドでやっているので、特に問題はないです。 デプロイサービス特有の設定を覚える必要がないので楽ですよ。 それが他の人に合うかはわからないですが、Hugo なら例えば以下のようにコマンドを打つだけでOKです。
hugo
drop-inline-css -r public -o public
minify -r public -o public
これだけで一番面倒な最適化が完了するので楽ちん。

2022年2月5日土曜日

Emoji Concentration を作った

Emoji Concentration を作りました。英語の神経衰弱ゲームです。 日本語版もあるので小さな子の語彙学習ゲームとしても使えます。



神経衰弱は英語学習では割とよく使われるみたいなのでササッと作ってみましたが、 覚えさせるフェーズが確実にあるので、確かに良い気はします。

一人でもプレイできるようにしましたが、何人かでプレイしたほうが面白いかも。 他のゲームと違ってスコア表示しにくいのが、唯一の欠点ですかね。

2022年1月23日日曜日

Emoji Clicker を作った

小さな子向けの英語学習アプリ Emoji Clicker を作りました。 選択肢の中から絵文字をポチポチして英単語を覚える、ゆるふわ学習アプリです。



英語はタイピングや手書きのアプリをたくさん作ってきましたが、小さな子はポチポチ系のアプリも向いてます。 絵と音声と文字をセットにして覚えるポチポチ系アプリを作りたいと思っていました。 そして作るときは外部依存性がゼロで、好き放題できるアプリにしたいと思っていました。

見てすぐに意味がわかる絵のデータセットさえあれば作れるので、絵文字で英単語を覚えられるようにしました。 わかりやすい絵文字と単語に絞っていますが、それでも 700単語くらいはあるので、小学英語に必要な単語数はほぼクリアできます。

かなり絵文字を絞りましたが、それでも幼稚園くらいの子がやろうとすると、想像が付きにくいものはある気がします。 語彙力は人それぞれで完璧な調整は難しいため、カテゴリを選びながらやると良さそうです。 まずはスポーツや食べ物が良さそう。 個人的には英語学習を想定して作りましたが、日本語版もあるので、幼稚園くらいの子には日本語の語彙力チェックとしても良いかも知れません。 日本語で軽くポチポチして慣らした後、英語でポチポチするのが良さそうかなあ。

割と色々な使い方ができそうで、まあまあ便利かも。 漢字をどこまでひらがなにするかも難しいですが、なるべく教育漢字に収まるようには心掛けました。 音声読み上げも絵もあるので、全部ひらがなにしなくても一応なんとかなる気がします。

2022年1月21日金曜日

国旗クイズを作った

ふと簡単に作れるなあと思ったので、国旗クイズ を作りました。



海外の国旗は特に習う機会もないので、遊んでみると全然わからないものですね。

割と汎用性がありそうなアプリなので、多言語対応もしておきました。 最近気付いたのですが、<html lang="en"> の lang 属性を使って JavaScript の処理を変えると、簡単に対応できますね。

2022年1月1日土曜日

2021年発表作品で特に面白かったフリーゲーム

フリゲ2020 が発表されていました。 やっぱり強かった ASTLIBRA & まどTAS。 この2作品はまず入ってくるだろうと思ってました。
  1. ASTLIBRA ~生きた証~ (紹介記事)
  2. まどか☆マギカ -Try Another Story- (紹介記事)
  3. 氷水火山SOKOBAN (紹介記事)
  4. ミカゲ・キューブ (紹介記事)
  5. 忍屋 (紹介記事)
※年度を間違えた作品を修正しました。





ASTBRA & まどTAS が凄いのはもう明らかですが、12月にたくさん新作が出てきて、プレイしきれていないものが結構あります。 10年以上制作している方がたくさんいて、ただだだ凄さを感じる年でした。 10年も作っていると、ストーリーもコードも、破綻しないように作るのだけで大変な気がします。 ASTLIBRA はタイトル通り、まさに生きた証。 HP を見ただけで感動することって、なかなかないような気がします。

今年はあまり記事は書いてないけど、 小さな子に良さそうなフリゲをまとめてみた というページを作りたくなって、 裏でたくさんのパズルゲームや、ミニゲームをプレイしたりしていました。 「これ面白いよー」と他人にもプレイさせたりしていたのですが、 最近の派手なゲームをプレイしている人ばかりでもパズルゲームは割と人気があります。 シンプルでも好まれるゲームづくりを考えさせられました。

記事を書いている時より、振り返ってみるとこれは凄かったなと思うものもあります。 その一つが ミカゲ・キューブ で、カードバトルの作品として、かつてなく面白かったです。 同じ作者様が、レシアの救済、永遠の夢。 というカードバトルの作品を作ってますが、こちらも面白かった。 低行動力と高行動力をうまく逆転させるようなシステムは、どこかに使えそうです。

最後に、記事は書いていないですが (書けるものは書くかも)、プレイして面白かった作品をまとめておきます。