月と燃素と、ひと匙の砂糖こうして過剰なものの蕩尽は、それが皆の前で誇示され見せびらかされることによって、世俗的な権力や権威へと転化する。することによって、それは人々の社会的な位置を確定して社会の秩序を保つ役割を果たすのである。Fujisakiこんにちは!東大は今、年二回ある学園祭の一つ、駒場祭の真っ最中です。前編」を頒布いたします。ソースコードCD付属♪プログラマなら誰でも憧れる(?)ファミコンエミュレータを、ハードウェアの直接的な解析なしに、インターネット上の情報だけで実装しよう!という本です。ハードウェアの解析をしなくても、こんなエミュレータを開発できてしまいます!ただし今回は前編までで、カートリッジ・RAM・CPUの実装だけとなっています...。ページ数の制限もある中で、できるだけわかりやすく解説したつもり...です。解説は途中までですが、ソースコードは完全版が付属CDに収録されています。東京渋谷の近くにある、東京大学駒場キャンパスで駒場祭は開催されています。大きな地図で見るこの駒場キャンパスの12号館3階1233教室というところで頒布しております。サークルとして出ているわけではなく、本部での委託販売となっています。当日は、私自身はこのコミックアカデミー会場か、5号館二階523教室で跳梁跋扈している予定です。■なお、日曜日は東大ニコ研コミュニティで「まるきゅう」のダンスのネット中継も行います。そちらもよろしくお願いします。Fujisaki以下の記事の続きです。■abstract@nebutalabさんの追試結果を踏まえて、私もベクトルデータの解像度を下げる効果について検討しました。その上で、既存のライブラリ(Webkit/Chromeに組み込まれているライブラリ)と比較を行いました。ベクトルデータをlogを用いて圧縮する方法を検討したところ、既存のライブラリと同程度にまでメモリ消費を下げることができました。最後の仕上げとして、autotoolsを用いたライブラリとしてまとめて公開しました。■解像度を下げてもなお特徴的なベクトルデータ@nebutalabさんのエントリでは、二次元でなく一次元での推定テストを行った他、二次元の場合に、ベクトルデータの解像度を下げてどれくらい推定成功率が下がってしまうかのテストを行っていらっしゃいます。解像度をさげて正確性を犠牲にしてまで、ベクトルデータのサイズを小さくしなくてはいけないのでしょう?元のままだと、ベクトルデータが各エンコードごとに256×256=65536要素必要で、uint32(4バイト)で持っていると256KBも必要になってしまいます。Shift_JIS、EUC-JP、ISO-2022-JP、UTF8と4種類用意するとトータルで1MBです。実際に使おうとするとこのメモリ消費量の大きさがネックになってしまい、ソフトウェアの一部として組み込むには使いづらいからです。私の側でも解像度を下げたデータを作って見ました。うーん、16×16までは確実に差が分かるけど、8×8くらいから怪しいかなあ、といった感じでしょうか。■解像度を下げて実験!今回は、@nebutalabさんの方法を参考に、ページ内でcharsetを指定してあるページのみをテスト用デ-タに使うことで、ノイズを排除しました。あと、方式は前回検討し疑似コードでの方式をベースに、charCntはどれかの文字コードのスコアが0より大きい物(判定として使える物)にしました。解像度を下げていくと、これは最初の擬似コードの方式と殆ど同じになります†1。意外にも8×8では十分判定(100バイト時点で99.907%)でき、4×4で怪しくなってきて、2×2では全然だめ、という感じのようです。guess(25%)よりは高く、7割程度分かってしまうようです。...確率って凄いですね。@nebutalabさんのものよりも成績が良いのは、おそらくやっぱり辞書データのサイズの差だと思われます。Wikipedia全文データはやはり広大みたいです...。99%以上の部分についてのみのグラフがこちらです。128×128で何故か収束が悪いですが、おおむね100バイト収集すれば収束するようです。len)と、性能を比較しました。コメントから、このコードは、JVIM由来だそうです。具体的に実装を見るとわかりますが、このコードもヒューリスティック†3なアルゴリズムですが、それぞれの文字コードへの深い知識を使って実装されており、今回のアルゴリズムとは対照的といえます。どちらのコードも、最初の1バイトで高い性能を示し、いったんガクっと落ちる点など、非常によく似た性質を示しています。8×8の辞書サイズでは最初の方のデータのグラフが安定しませんが、おおむね同じような収束曲線を示しているように見えます(私の贔屓かな...)。JVIMの実装はShift_JIS/EUC-JP/ISO-2022-JPの三種類しか判定できないので、そういう意味では私の勝ちです(ぇこちらも99%以上の部分を拡大したものがこちらです。100バイト使うことが出来れば、わずかにですが、このwebkitのコードより良い性能を示しています。(合計で22876件のテストデ-タを使いました。誰か検定してください(えー))実行時間の殆どはIOなので実行速度は問題にならないと思いますが、webkitのコードよりも確実にコード量は少なく(=省メモリ)て済んでいます。■さらに、Logを使って圧縮しよう。8×8にまでベクトルサイズを下げることで、ベクトルの要素を32bitのfloatで持つと、8x8x4(bytes)*4(種類のエンコード)=1024バイトにまでベクトルデータを削減することが可能ですが、これでもwebkitの実装の256バイトよりはだいぶ大きくなってしまいます。できれば、同等のメモリ消費量程度にしたいですよね。各ベクトルの要素を1バイトで表現するようにデ-タを圧縮する事ができれば、webkitの実装と同じ、256byte前後でベクトルデ-タが収まります。圧縮すると、展開のために少し速度が遅くなると思われますが、処理速度の大半はIOな上に、アルゴリズムは現状非常にシンプルなので、多少遅くなっても問題無いでしょう。データの分布を見て、特性を観察しましょう。ベクトルの要素のうち、最大のものを100%として、0~1%の間の大きさの要素が何要素あるか、1~2%の間の大きさの要素が何要素があるか...という、ヒストグラムを作ってみました。!?...どうやら、殆どのベクトル要素は、非常に小さい部分に集中しているようです。なると、もし、ベクトルの要素の最大値を255とした場合に、各要素がどれくらいの割合か†4、でデ-タを表すことで圧縮してしまうと、圧縮後の要素が0~2の要素ばっかりのベクトルになってしまい、なんだかもったいないデ-タの使い方になってしまいます。解凍†5して元の要素を復元したときに、元々色々な値を持っていたはずの、値が小さい範囲の要素が、みんな同じ値に復元されてしまうことになります。デ-タの使い方がもったいない上に、精度も悪い圧縮になってしまうので、これでは圧縮としては使えません。いう風に圧縮しましょう?小さい要素の差がつぶれて、みんな同じ値になってしまうと、もしテストデ-タを比較するときに、小さいベクトル要素ばかりが出現した場合に、みんな同じ値になってしまうので比較できなくなってしまうだろう、と思われます。逆に、非常に大きい値は、他を圧倒しているため、多少ズレてしまっても文字コ-ド別に順位を取った際の順番はあまり変わらないだろう、と考えられます。圧縮するなら、小さい値をできるだけ正確に保存して、大きい値は割と目をつぶる...そんな感じが良いでしょう。Alpha先生に書いてもらった図です。Log(x)は、xが小さい範囲では急激に増加しますが、大きくなってくると増加が緩やかになります。この関数を通すことで、値の小さい範囲の差を拡大し、大きい範囲では逆に縮小する事ができるのです。いうわけで、ベクトルの各要素にLogを取った後に同じようにプロットした結果がこちら。?だいぶ、グラフ上の色々なところに値が出現するようになりましたよね†6。Logを取った結果に、さらにLogを取るという二重対数というのもやってみましたが、これもそれなりに良い感じです。これを、最大値を255、最小値を0として表して†7、ベクトルデ-タを圧縮しました。圧縮したデ-タを、再度元に戻した場合に、元のデ-タからどれくらい”ズレ”てしまってるかを計りました。具体的には、復元後のベクトルデ-タの各要素の合計を、元デ-タの各要素の合計で割りました。0.574984371330254%やっぱり何もしないのでは全然駄目でしたが、対数を使うととても正確で、二回対数にするとさらに少し改善するみたいです。どちらにしてもでも1%以下しか変わらないため、きっと良い結果が得られると考えられます。■webkitと同程度のメモリ消費に抑えても、同じ程度の性能が出るその結果がこちら。?一回対数の場合、1バイトだけ使ったときの推定成功率は大きく下がりましたが、元のグラフから殆ど外れないグラフが書けました。100バイト使った時の成功率は、元の結果と全く同じ。デ-タを1/4に圧縮しましたが、副作用はありませんでした。二回対数は正直かなり重いので、実装するには一回対数を使って圧縮すると良さそうです。■C言語のライブラリとして実装しよう!&autotoolsの使い方githubで公開中!さてさてさてて。すべての条件がそろいました。十分な判定率と十分なメモリ消費量、それでもシンプルなアルゴリズムを用意できました。これをライブラリにしない手は、ありませんよね!?とてもシンプルなライブラリですから、せっかくなので、autotoolsの使い方も調べてみましょう†8。installなパッケ-ジ作ってみたいですよね!ね!!(とりあえず、こんな構成でコードを書きました。len);こんな感じのシグネチャが入っています。src以下はその具体的な実装です。cset_guessはcsetguess.cに入っています。これからautotoolsを使って、ビルド環境を作りましょう。autotoolsは、仕様が頻繁に変わってしまうのか、公式のドキュメントを見ても、なんかよく分かりません...。一応、作業の流れはWikipediaの図が参考になりました。Googleで検索するときも、できるだけ最新の情報を見るのをお勧めします...。トップディレクトリにMakefile.amを、こんな感じで作ります。\include/csetguess.hincludedir=$(prefix)/includeinclude_HEADERS=include/csetguess.h次に、autoscanをして、configure.scanをつくり、これをconfigure.acにリネームします。[BUG-REPORT-ADDRESS])となってるところを、とりあえず自分のものに併せて変更しました。[nasda60@hotmail.com])Wikipedaの図にそって、aclocal、autoheader、automakeします。-cというオプションを付けます。again.た、たくさんエラーメッセージが出てしまいましたが、これらを地道に修正します。libraries.AM_INIT_AUTOMAKEの引数がよく分からないのですが、とりあえずこれで良いみたいです(そもそもこのAM_INIT_AUTOMAKEの解説、古いのでは...?よくわかりません...)。修正したところで、再度aclocalから行って行きます。-cうまく行きました。最後にautoconfすることで、ついにあのconfigureスクリプトが生成されます。autoconfさて、これでconfigureしましょう。installができるようになります。以上をまとめると、Makefile.amを書くautoscanして、configure.scanを生成し、configure.acにリネームして、編集する。以下のコマンドを実行していく。-cautoconf./configureスクリプトができて、晴れて完成。はしょりまくりですが、とりあえずの雰囲気掴みには...なってると良いな...。Makefile.amとconfigure.acさえあれば全部自動生成されるので、この二つ以外はリポジトリに入れなくても大丈夫です。■ブラウザに実装しないの?技術的障壁が二つほど横たわっております。Chrome/Firefoxともに、拡張機能からページのエンコ-ド設定を変更する方法が見当たらないペ-ジのテキストを、拡張機能からバイナリとして取得する方法が見つからない後者は、Ajax的にリクエストを投げて、その結果をバイナリとして受け取る事でできそうなのですが、わざわざ再度リクエストを投げるのは計算機資源の無駄だと思います...。■まだちょっとやり残した気がすること今回のJVIM由来のwebkit内のライブラリは、実はいつも使われるわけでなく、HTTPヘッダやmetaタグで指定したエンコードと実際のエンコードが食い違っている場合にのみ使われます。一切無指定だった場合はICUというUnicodeのライブラリを使ってエンコードの推定を行っています。このICUもずいぶん高性能なライブラリのようなのですが、どうしてChrome/Webkit/Safariは文字化けが起きがちなのか、デバッグして調べたかったのです...が...。PCのメモリがどうも足りないみたいで、出来ませんでした。実験に用いたソースコードをgithubで公開します。読む価値はないと思いますが、一応証拠です。使ったスプレッドシート:記事では使わなかった数字とかも、とりあえず全部入ってます。これも一応証拠として。エンコードが無指定な時には使われません。情報科学っぽく言えば、エントロピ-が高くなりました。Fujisaki先週土曜日より、独自ドメインledyba.orgへの移転を行いました。Doがサービスを停止するまでは、リダイレクトを提供します。(こうして”歴史的経緯”が増えていく...w)■RSSの移行をお願いします!RSSのアドレスも、Blog移転に伴い変更になりました。以前のアドレスからもアクセスできるようにはしていますが、できれば移行をよろしくお願いします。以下のリンクをクリックする事で、新しいフィ-ドをあなたのRSSリ-ダに登録できます。Yahoo!BlogLinesこちらのサイトを参考にボタンを作ってみました。■メールアドレスも変更になりましたメールアドレスも、今までpsi@science.mi.to†1を使っておりましたが、今回変更になりました。BBQ“が終了になってしまいました。そのためどこかへの移行が迫られるわけですが...正直、もうメールアドレスを変えたくありません!そして、「そういえばledyba.ddo.jp/も無料サービスだったなあ...」と思い出しました。ddo.jpがサービスを終了すると、メ-ルアドレスと同じように使えなくなってしまうという可能性に、...やっとこさ気づいたのです!orzメ-ルアドレスの乗り換え先に新たなサ-ビスを探すよりも、ドメイン名を取ってしまった方が良いだろうという結論に至り、ドメインを移行しました。Fujisaki前回までのあらすじ。文字化けをどうにかしたい。それぞれのエンコードは、バイトとバイトのつながりに特徴がある。(数バイトで一文字表すから)これを、ベクトルに見立てて、それぞれの「角度」を調べて、一番近いので分類してみた。!前回、RFCの全データを使って作ったASCIIのデータがありました。これがASCIIの使われている領域ですから、これを判断に使わなきゃ良いんじゃないの?...というわけで、ASCII部分を除外して作ってみた各エンコードの画像がこちら。(クリックすると拡大します、ぜひクリックしてみてください)ASCIIと使用領域がかぶっているISO-2022-JP(JIS)でもちゃんと要素が残っているので、使えそうです。安直な方法†2ですが、とりあえず実験です。■それで実験してみるどうです!どの素材データを使っても、安定して分類出来るようになりました!ASCIIの場合、全部のエンコードに対する角度(cosθ)が0になるので、これで判断が出来ます。0201†3なのかは判断できません。安直な方法でしたが、効果はあるようです。■さらにパフォーマンスを高めたい?現状の方法では、特徴ベクトルの作成と、ベクトルの距離の比較の二つのパートから成り立っており、それぞれの計算量は特徴ベクトル作成:文章の長さnに対してO(n)ベクトルの比較:文章にかかわらずO(1)であり、計算量は比較的少なくて済みます(ただし計算量理論的な意味で)。いうのも、ベクトルの比較はO(1)ですが定数項が大きい(65536回のかけ算計算)です。ASCIIを除外し、更にすべてのエンコードで0になっているところを除外したところ、43394つの座標軸†4(約66%)に減らせましたが、それでも多いと言わざるを得ません。■ファイルの長さはどれくらい必要なの?ベクトル比較のO(1)は難しそうなのでとりあえず放置して、まず特徴ベクトルの作成をどれくらいケチれるか実験してみましょう。直感的に考えて、特徴ベクトル作成に使う文字数を増やしていっても、どこかで成功率が飽和していく事が予想されます。Wikipediaの全文のソースを素材データ†5とし、青空文庫のうち、十分に長い小説2500本を使って実験を行いました。青空文庫は全文Shift_JISですが、それをiconvを使ってそれぞれのエンコードバージョンを作りました†6。文章の1000バイト目†7から1バイト、2バイト、3バイト...100バイトと切っていって、それぞれで推定を行わせ、成功率を測定しました。20バイト前後(つまり、日本語で言うと10文字程度)で飽和し、それ以降は十分な精度が得られるようです。青空文庫は非常に綺麗な日本語であることを考えても、100バイト(日本語で50文字前後)もサンプルを集めれば十二分でしょう。余談として、ISO-2022-JPとUTF-8は若干グラフの立ち上がりが悪いですが、これはUTF-8が他のエンコ-ドと違い、日本語1文字を表すのに3~4バイト必要なこと、そしてISO-2022-JPはASCIIとかなり近い領域を使っていることが関係しているのかもしれません。■日本語が50文字もあれば十二分な精度が出る→計算量を減らす工夫が可能に!さて、この結果から、50文字程度の日本語を集めてくれば、十分推定可能だろうという事が判明しました。この結果を生かすと、65536回もかけ算をしなくてもコサイン類似度の比較が可能だと考えられます。具体的には、コサイン類似度を次のようなフローで求める事で、O(1)かつ十分に小さい定数項の計算量でにエンコードの推定ができ、以上の実験結果と同じ結果が得られ、十分な推定精度が得られるのではないでしょうか?■ほぼO(1)に高速化した推定フローまず、それぞれのエンコードのベクトル、V[SJIS]、V[EUC]、V[JIS]、V[UTF8]を用意しておきます。ベクトルの各要素を長さで割って、長さ1の単位ベクトル†8にしておいてください。この時、Shift_JISのベクトルの、1バイト目がAで、2バイト目がBの要素にはV[SJIS][A][B]のようにアクセス出来るとします。スコア決定のために使う変数、score[SJIS]、score[EUC]、score[JIS]、score[UTF8]、そしてサンプルを集めた数を記録する変数charCntを用意しておき、すべて0で初期化します。文章を先頭から1バイトずつ見ていきます。文章中に非ASCIIな文字が現れたら、ループに入ります。現在のバイトと、一つ前のバイトを記録し、一つ前をX,現在をYとします。V[SJIS][X][Y]のように、それぞれのエンコードのscoreに、対応するベクトルの値を加えます。charCntに1を加えます。charCntが(例えば)100†9を超えたら、ループを終了します。現在のバイトがASCIIなら、ループを終了します。charCntが100を超えていなければ、2へ戻り、再びその位置からバイトを見ていき、次の非ASCII文字に備えます。それぞれのscoreのうち、一番大きい値だったものが、この文章のエンコードと推定されます。ミソは、まず素材データのベクトルの長さを1に揃える†10こと、さらに、比較データのベクトルは作らず、直接内積を求めること、比較データのベクトルの長さで割らなくても、最終的な値の大小関係には影響を及ぼさない事†11です。■擬似コードわ、わかりづらいですね。Cっぽい疑似言語でかきます。//例えばvector[SJIS][0x30][0x21]とすると、//SJISのベクトルで、1バイト目が0x30で、2バイト目が0x21であるときの、ベクトルの要素が取れる//...というようなデータを事前に作成しておき、さらにベクトルの長さは各エンコードで揃えてください。score[];//SJIS,EUC-JP,ISO-2022-JP,UTF8の4つの要素を用意して0で初期化。print("このテキストのエンコードは"+trueEncode+"です。");}説明用に書いただけなので、効率はあまりよろしくありません。■さらに精度を高めたい?また、今回はあえて文字コードに関する知識を殆ど使っていない†12ので、これを使うことで精度が多少は上げられると考えられます。その知識をわからなかったらこういう統計的な手法に頼るべきではないかという気もします。■他に使えそうな分類手法は?■ベイズフィルタまず、SPAMでも使われてる、ベイズフィルタ(PDF)なんてどうでしょう?このPDFにも書いてある通り、文章から「トークン」を作り出す際に、二文字ずつに区切って使うという、まさに今回使ったのと非常に似た手法「bigram」も存在するし、スパムと違って「未知の単語」は存在しないので†13、使えるかもしれません。なんてどうでしょうか?分類するための方法ではないので少し違いますけど、正しいエンコードの時は1、間違ったエンコードの時は0を返すような係数(このページの例だとβ)を決定して、それを使ってみると良いかもしれませんね。追試していただけました!@nebutalabさんありがとうございます!辞書デ-タの解像度を下げる方法は、今度の休日に試してみる予定です。■実験資料データが記録された、odfファイルです。グラフの元データなどがあります。実験に使用したソースコード。めてC#使いましたが、なかなか良い感じ。Mono使えばLinuxでも安心。「ベクトル」に見立てたんでした。座標軸ということになります。格好つけて基底ベクトルでも良いけど。変換できなかった文字は無視しました。1でなくても同じ長さなら良いです。1でなくても、もちろん構いません。Fujisaki現在ではだいぶ少なくなりましたが、ネットサーフィン(死語)をしていると、たまに文字化け(これも最近は死語?)に出会いますよね。うう、なんて書いてあるのかさっぱりわからない。ChromeだとエンコードをShift_JISと思い込んでますが、本当はEUC-JPでした。今回は、この文字化けを回避する、”文字コードの自動推定”をやってみました。最近「プログラマのための文字コード技術入門」という本を読んでいます。この中では日本語圏ではおなじみの、Shift_JISとISO-2022-JP(俗に言うJIS)と、EUC-JP、そして最近圧倒的なシェアを誇るUTF-8(このBlogもUTF-8です)のそれぞれの符号化方式について、変換プログラムを書くのに十分な位に詳しく説明されています。詳細を見るどの方式も、ASCIIを基本として、ASCIIでは使われていないデータ部分を使って日本語(やその他の言語)を表現しています。その使い方はそれぞれの符号化方式で異なるため、「このデータの並びはShift_JISでしか使われないはず...だから、このテキストはShift_JISだろう」みたいな感じで、文字コードの推定ができます。昔のYahoo!JapanはEUC-JPで書かれていた(今はUTF-8)のですが、そのとき、ページの最初のほうにこんな感じのコメントが入っていました。0xFD」は、Shift_JISにもISO-2022-JPにも、さらにUTF-8にも決して現れないデータの並びです。ブラウザはこのデータの並びを見たときに、「あっ、これはEUC-JPなんだな。」ってわかる(から文字化けが発生しない)、という訳です。限界もあるこのようにすれば、確かに文字化けは回避できます。みんながみんなこういった気の利いた(?)コメントを入れてくれる訳ではありません(そもそもHTMLには、meta要素での文字コード指定があって、むしろ上の方法よりもこちらで明記しなければならないのですが...)。そんな時、文字化けしてしまうことがあります。A7は、そのままShift_JISと思って読み込むとエー瓏、ハオ槢ァとして解釈することができます。どのバイト列もShift_JISとしても有効なバイト列だからです。よって、このバイト列がEUC-JPの「完璧な牛丼」なのか、Shift_JISの「エー瓏、ハオ槢ァ」なのか、論理的に確定させることは絶対にできない、ということになります。(なおInternetExplorerはこの文字列をShift_JIS(文字化け)、ChromeはEUC-JP(正しい)として認識しました)最初の例も、meta要素でのエンコード指定も、気の利いたコメントも無いため、Chrome14はShift_JISと勘違いしてしまった...という典型的な例です。統計的手法を使おう!確かに、先ほどのバイト列は、Shift_JISでもEUC-JPでも有効なバイト列でした。?「エー瓏、ハオ槢ァ」ってまず無いだろうな~って、感覚的にはわかりますよね。「完璧な牛丼」だろう...と。こっちも意味不明ですけど...^^;この「”エー瓏、ハオ槢ァ”よりは”完璧な牛丼”の方が『ありそう』だよね」って感じをコンピュータに自動で判断させれば、もっと正確に文字コードが判別できるはずです。...そんなときは...そう!統計学の力を借りましょう!†3バイトとバイトの”つながり”を手がかりにするじゃあ、統計的手法を使って、「ありそう」かどうかを、どうやって判定すればいいのでしょう?日本語の文字コードは、どのコードも基本的に文字列を複数のバイトを使って(例:UTF-8だと大体3バイト、他のエンコードでは2バイト)表しています。この関係性を何とか使えないでしょうか?そこで今回は、バイトとバイトの”つながり”を手がかりにすることを考えてみました。65536通り)について調べ、その特徴を比べます。画像にマッピングそのデータを使って実際に判断させる前に、とりあえず使えそうかどうか、画像で可視化することで、調べてみましょう。今回は、Wikipediaの全文データを用いて、画像を作って見ました。X軸が1バイト目、Y軸が2バイト目です。出現頻度で色分けしてますが、適当です。「コサイン類似度」さて...人間は画像を見て一発で判断できますけれど、コンピュータはそうではありません。これをどうやってコンピュータに比較させれば良いのでしょう...うーん...画像認識...?今回用いたのは、「コサイン類似度」と言われる方法です。順を追って説明していきます。バイトのつながりのデータを、”ベクトル”に見立てるこの画像は二次元でマッピングされていますが、若干扱いづらいので、一列につなげて、65536通りの要素のベクトルとして扱うことにしました。プログラミングの観点から言うと、二次元配列から一次元配列に変えただけということになります。←65536次元(=256x256)のベクトルさて、ベクトルです。二次元美少女ばっかり追いかけてるのに、三次元だと非リアなのに、65536次元なんて不安?大丈夫、ベクトルの性質は二次元の時とま~ったく同じ!ご安心ください(要素が多いので計算が人間には無理ですけど...)。「推定したいファイルの”ベクトル”」と、Shift_JIS、JIS、EUC-JP、UTF-8のそれぞれの”ベクトル”を比較して、どれが一番似てるか調べる問題に、言い換えることができました。結局、似てる矢印を探せれば良いんです。“似ているベクトル”って、何だろう似てるベクトル...って何でしょう??とりあえず、図をかきましょうか。二次元と全く同じように扱えるって言いましたよね。普通に2次元絵で大丈夫です。矢印には、向きと長さがあります。長さが近いベクトルを似てるってするのは、どうでしょう?ほら、人間だって身長の高低で分類したりしてますし。長さって、ベクトルの各要素の二乗の和の平方根で表されるんでしたね(つまり、二次元だと長さ=√(x^2+y^2)でしたね。3次元なら√(x^2+y^2+z^2)、4次元でも、65536次元でも同じです)。今回各要素の値は、文章内に現れたバイトの繋がりをカウントしたものでしたから、文章が長ければ(=たくさんのデータを使っていれば)、今回のベクトルの長さも長くなる...ということになります。これでは、比較には使えなさそうです。いうわけで、必然的に向き、つまり間の角度を比較することになります。え...65536次元空間での角度って、何...?2つのベクトルの間の、角度を求めるには?まだ焦るような時間じゃない(AA略)。何度も繰り返すように、二次元と同じです。懐かしの高校数学のおさらいです。「成分だけ」を使ったもの「長さと間の角度」を使ったものの、二通りを使って表せるんでした。上で見たとおり、「長さ」も成分から求められますから、「成分だけ」を直接使って求めた内積と比較することで、cosθがわかります。(式のとおりです)このcosθのことを、「コサイン類似度」と呼びます。角度でもいいのですけど、やっぱり65536次元での角度ってよく分からないし、空間の話でもないので別名が付いてるんだと思います。-1ですから、この値が大きいほど、2つのベクトルの向きは似ている、ということになります。実験結果!さて、以上の道具立てを使って、調べてみました。Wikipediaの全文データ、青空文庫のデータ、あと各種ウェブサイトをクロールして得たデータ(たまに違うエンコードが混じっている)†4を元にして、それらとは別にクロールしたウェブサイト†5の文字コードを判定させた結果がこちらです。クロールしたものである以上別のエンコードやASCIIのみのデータが混入しているので、95%以上で判定できれば十分な精度だと考えています。データを集めるうまい方法が思い浮かばなかったんです。†6))青空文庫の素材データは、ほぼ100%の精度でエンコードを判定してくれています!失敗していると出た結果のところも、実際に見てみると殆どは混入しているノイズで、「むしろ正しく判定している」ものが殆どでした。これChromeに積むのってどうでしょう!?Wikipediaは...utf-8はちょっと苦手、みたいですね。実際のウェブサイトをクロールして得た素材データは...うーん。「現場を反映している」と思っていたのですが、まさかの大苦戦...。これは、どういうことなのでしょう??クロール素材データ使用時では、特定のウェブサイト*だけ*失敗してる?この結果には表れていないのですけど、ログを見ていて気になったのが、クロールによって得た素材データを用いて推定した際、特定のウェブサイトにおいて*だけ*失敗していることが多いということです。SJISとUTF-8について、サイト別の判定率も出してみました。(ニュースサイトが多いです。記事があって、エンコードが統一されてて...と選んでいくと、新聞社のページが残りました^^;)■SJIS(クロール素材データ使用時)房日新聞の判定成功率の低さがすさまじいです。■UTF-8(クロール素材データ使用時)エキサイトとCNETはうまくいくのですけど、他が...。2つの文字コードが混じり合っている気になるのは、どの文字コードも、英語を表すためのASCII(どれでも共通)と、日本語を表すための方式(それぞれ異なる)が入り交じっていることです。二つの要素が入り混じってしまっているため、これが問題になっているのかもしれません。これだとどのような問題が起きるかというと、コサイン類似度の比較によって、日本語バイトデータの出現頻度の比較だけでなく、「ASCIIと日本語が、それぞれ文章内に含まれている割合」が比較要素に入ってしまう可能性があります。SJISの素材データはASCII多めEUC-JPの素材データは日本語多めという状況で、ASCIIが多めのEUC-JPの比較データを判定させた場合、SJISに判定されやすくなる†7...などの可能性が考えられる、ということです。†8Wikipediaや青空文庫の素材データは、あくまで「同じテキストデータ」を、「別のエンコード」で示したデータを使っているので、ASCII/日本語の割合は、すべてのエンコードにおいて同一です。そのため、比較データの日本語/ASCIIの割合の影響は、打ち消されるのではないでしょうか...?とりあえず、ASCIIもマッピングして可視化してみる?とりあえず、RFCの全データを取得し、同様に画像化してみましょう。...RFCってこんなにあったんだ...(--;■ASCIIの出現頻度マッピング左上の一カ所に固まってますね。これがASCIIの特徴です。これが、日本語でのデータと混ざってしまうことで、悪さをしてしまうようです...。ASCIIと、素材データ、そして失敗した比較データを並べて眺めてみるWebをクロールして得られた素材データと、特に失敗しているwww.bonichi.comのデータを並べた画像を作ってみました。ほらほら!どの画像でも、左上には同じようなパターンが広がってますよね。前述の本での例をそのまま使いました。