たにしきんぐダム

プログラミングやったりゲームしてます

京都から神奈川へ引っ越し

2020年9月19日に京都符京都市を離れ神奈川県川崎市に引っ越します。(入居は20日)

京都には京都大学入学時に福岡から越して来て、そのまま京都で就職し、合計で7年半過ごしたことになる。

主な引っ越し理由はこれです。アルバイトと貯金と奨学金(未定)でなんとかする予定ですがどうなることやら(良い子はちゃんと十分な貯金を作った上で進学してね)。金の話は1年半くらい後にブログ書きたい。

様々な事情で4月入学です。

乞食んぐ24

www.amazon.jp

引越し先

  • 具体的には日吉~元住吉~武蔵小杉らへん。
  • 東工大へのアクセスも良いし、渋谷周辺へのアクセスも悪くない好立地(もっとも最近はコロナの影響で関係ないけど)。
  • 家賃水準も割と安め、京都中心部(四条~御池/烏丸あたり)と大して変わらない。
    • 京都で住んでた家が烏丸御池のど真ん中だったので高かったのもあるが、引っ越しによって家は広くなり家賃は安くなる。
  • 日吉は山の上だが、元住吉・武蔵小杉は海抜が低く、多摩川鶴見川に囲われているのでハザードマップは要確認

京都(市中心部)で7年半過ごした感想

  • 👍
    • 自転車で移動しやすい(街の中心部が平、端っこは少し山がちな程度)(街がコンパクト)
    • グリッドレイアウトな計画都市なので移動しやすい(ある点からある点への最短経路の数が多い)
    • 自然が多い (鴨川や京都御所など)、山が近いので山陰がいつも見れる。
    • 寺社仏閣が多いので、そういう建築物が好きな人には嬉しい
    • 大阪や梅田へのアクセスが良いのでライブなどにも行きやすい
    • 観光地を避ければ(東京都比べて)人が少ない、人混みが非常に苦手なのでありがたい
    • 美味しいパン屋が多い 「パン消費量日本一」京都人の意外すぎる食事情
  • 👍👎 (良くも悪くも)
    • 家賃はそこそこ(東京や大阪と比べたら安いけど、他の地方都市と比べると高い)
    • 景観条例があるので奇抜な外観の建物がなく景観が整っている。(個人的には好きだが、人によっては退屈かも)
  • 👎
    • 高さ規制の影響でオフィスビルが少ない(ので企業が京都に大きなオフィスを作りにくく、雇用が発生しにくい)
    • 土地が空くとすぐホテルが建つ(のでオフィスビルが...)
    • バスがいつも観光客で激混みで使いにくい。(し基本的にバスより自転車のほうが早い)
    • 魚介類(スーパーの刺し身とか)の質がちょっと低い気がする(これまで宮崎県宮崎市->福岡県福岡市と港町付近に住んできたので魚の味にうるさいんだ)
    • 冬は寒いし夏は暑い(九州出身なので冬寒いのは当たり前だけど、夏は宮崎福岡より京都のほうがじめじめ暑くて過ごしにくい)
    • 海が遠い(港付近育ちなので海が好き、泳げないけど)

京都人は排他的だのなんだのみたいな話を聞くが、7年半過ごしてそういうのが気になったことはなかった(一人暮らしの賃貸だからかも)


京都はいろんな楽しい思い出があるが流石に7年も住んでると飽きてくるのでいい頃合いで引っ越しできたなと思う。さよなら〜

Coursera / Neural Networks and Deep Learning 受講メモ

Deep Learning を勉強しようと思い、Coursera の Deep Learning Specialization を受講し始めた。

  • ある手法がうまくいく/うまくいかないことのイメージを説明してくれたり、実装に際してのtips and tricksも教えてくれるのが良い。
  • 解析や線形代数を知らない人にも門戸を開くために、コスト関数やactivation functionの微分の計算などは答えだけ提示している。(良いと思う)
  • 穴埋め形式ではあるのものの、Jupyter Notebook 上で自分で Neural Network を実装する課題があって面白い。

www.coursera.org

この専門講座は5つのコースから構成されていて、Neural Networks and Deep Learning はその1つ目のコース。内容としてはロジスティック回帰、単層ニューラルネット、2層ニューラルネット、L層ニューラルネット

授業で聞いたポイントや、授業で触れられていない数学的な部分をメモしておく。授業内容そのものについてはあまり触れない。あくまで個人用のメモ


Python Basics with numpy

shape

np.random.randn(5, 1) # shape = (5, 1) 5 x 1 行列
# [[ 1 ],
#  [ 2 ],
#  [ 3 ],
#  [ 4 ],
#  [ 5 ]]

reshape

numpy.org

v.shape  # (x, y, z, examples)
v_flattened = v.reshape(x * y * z, examples)
v_flattened  # (x * y * z, examples)
# 0-th data を除いて flattening する
# ドキュメントにも書いてないように見えるけど...?
# The "-1" makes reshape flatten the remaining dimensions
ndarray.reshape(ndarray.shape[0], -1)

機械学習のコードを書いているとndarray(や pandas DataFrame)のshapeが合わずに実行時エラーが起きることが多いので、適宜shapeに対してassertionを挟んでやるとデバッグしやすい。

assert(a.shape == (5, 1)) 
assert(a.shape[1] == b.shape[1])

rank-1 array

stackoverflow.com

a = np.random.randn(5)  # a.shape = (5,) <- rank 1 array
# rank 1 array は column vector としても row vector としても振る舞うので扱いが難しい
# shapeが明示的に決まったndarrayを使うのが吉

a.reshape(5,1) # などで明示的な行列に変換可能

axis

numpy.org

Axes are defined for arrays with more than one dimension. A 2-dimensional array has two corresponding axes: the first running vertically downwards across rows (axis 0), and the second running horizontally across columns (axis 1).

x = np.arange(12).reshape((3,4))
# array([[ 0,  1,  2,  3],
#        [ 4,  5,  6,  7],
#        [ 8,  9, 10, 11]])

x.sum(axis=0, keepdims=True)  # running vertically
#[[12 15 18 21]]

x.sum(axis=1, keepdims=True)  # running horizontally
#[[ 6]
# [22]
# [38]]

dtypes

NumPyのデータ型dtype一覧とastypeによる変換(キャスト) | note.nkmk.me

broadcasting

ndarray と、スカラー値もしくはある方向の要素数が1のndarray(他の方向のshapeは一致してる必要がある)との間で演算を行う場合、自動的に値がコピーされ、shapeが一致するように計算される。

numpy.org

When operating on two arrays, NumPy compares their shapes element-wise. It starts with the trailing dimensions and works its way forward. Two dimensions are compatible when 1. they are equal, or 2. one of them is 1

a = np.array([[1, 2, 3],
              [4, 5, 6]])
a + 1
#[[2, 3, 4]
# [5, 6, 7]]

b = np.array([[100],
              [100]])
a + b
#[[101, 102, 103]
# [104, 105, 106]]

vectorization

行列計算には numpy.dot 等を使い、explicit for-loop を使わない。

import numpy as np

a = np.random.rand(100000)
b = np.random.rand(100000)

c = np.dot(a, b)
# 早いし、ベクトルのサイズ増やしても速度は変わらない(O(1))
# for loop だと O(N) の計算量になる。
# numpy vectorization では内部で SIMD命令が(利用可能なら)使われるので高速

towardsdatascience.com


Logistic Regression

授業では数学的な部分はあまり無かったので、はじパタを参照しつつ導出

はじめてのパターン認識

はじめてのパターン認識

  • 作者:平井 有三
  • 発売日: 2012/07/31
  • メディア: 単行本(ソフトカバー)

f:id:tanishiking24:20200911205315j:plain
2クラス分類のロジスティック回帰の計算

f:id:tanishiking24:20200911210503j:plain
ロジスティック回帰の多クラスへの拡張

softmax の微分は手を動かすとわかりやすい

further reading: The Softmax function and its derivative - Eli Bendersky's website

最適解を解析的に導出できないので、gradient discent や 共役勾配法で最適解を探索


Shallow Neural Network (~2層)

Activation function 色々

  • activation function には非線形関数を利用する。
    • activation function が線形だと、多層回路にしたところで等価的に1層の回路と同じ表現力しか持たない(超平面で分離しかできない)
    • sigmoid / tanh / relu / leaky relu など
    • relu や leaky relu などがよく使われる (sigmoid や tanh だと勾配消失問題で困る)(これは次のコースで詳細が説明されるはず)

この動画わかりやすい & 面白い

youtu.be

結合係数はランダム値で初期化する(0で初期化しない)

  • すべて0で初期化すると、後続のレイヤーの素子への入力及び出力はすべて同じになってしまう。
  • その結果学習をすすめたところで各レイヤ内の素子からの出力はすべて同じになり、素子の数を増やしてもニューラルネットの表現力は向上しない。

Loss function の結合係数による微分

Loss function には MSE を使ってるけれど、授業では交差エントロピー誤差使ってる。どのLoss functionを選べばよいかは後で

f:id:tanishiking24:20200911224228j:plain

f:id:tanishiking24:20200911222239j:plain

f:id:tanishiking24:20200911222243j:plain
g は シグモイド関数

多層ニューラルネットでは隠れ層-出力層の結合係数だけでなく、入力層-隠れ層の結合係数も当然学習する必要があるのでそっちでも微分

f:id:tanishiking24:20200911222246j:plain


Deep Neural Network

層を重ねることでニューラルネットの表現力が上がる直感

  • 入力層に近い層で細かい部分の特徴を捉え、それを合成していくことで表現力高く特徴を捉えることができる
    • 例えば画像認識の場合は、入力層に近いところではエッジなどが検出され、それを合成していくことで最終的に捉えたい複雑なパターンを認識できるようになるイメージ
  • とはいえ隠れ層を増やせばいいわけではない
    • 計算コストが大きくなる(当然)
    • オーバーフィッティング
      • ホールドアウトearly-stoppingや正則化項の導入でオーバーフィッティングを防ぐ(詳細は次のコースで)

Deep Neural Network での forward / backward propagation

基本的に2層ニューラルネットと変わらない(計算する数が増えただけ)

f:id:tanishiking24:20200912020052j:plain
Deep Neural Network の forward / backward propagation

書いている通り、後ろの層で計算した微分を、その前の層の微分にどんどん利用していく。こう書くと誤差逆伝搬というのがなるほどたしかにという感じがする。

行列による微分に慣れないので、バッチ処理しない場合でベクトルによる微分を考えてもわかりやすい。


メモ

Loss Function には何を選べば良い?

youtu.be

stackoverflow.com

パラメータの初期化など

次の Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization | Coursera で詳しく説明されるが簡単に

  • 結合係数の初期値は小さく設定する
    • 授業では np.random.randn(n2, n1) * 0.01
    • 結合係数が大きいと、linear product の値が大きくなる。その結果 sigmoid や tanh の勾配は非常に小さいものとなり学習が進まない(勾配消失問題の原因の一つ)
    • 同様の理由でデータの値も正規化してやると学習が早く進む。

ローザンヌ旅行記

2019年の6月に ScalaDays 2019 参加のためにスイスのローザンヌに一週間ほど滞在しました。カンファレンスレポートブログは書いたのだけれど、個人的な旅行記は書いてなかった。

developer.hatenastaff.com

ざっくりLausannneに一週間滞在した感想

  • (物価が高いのもあり)治安はめちゃ良いし、人も親切で街も清潔。
  • とにかく坂が多い(長崎みたいな感じ)で歩き回ると異常に疲れる。
    • 最後の方は歩きすぎて足にまめができて辛かったのでUber使ったりしてしまった...
  • 6月だったからか?日が落ちるのが非常に遅く21:00くらいから少しずつ暗くなってくる。
  • 英語は意外と通じないので長期滞在するなら最低限フランス語使えると楽そう(僕はメルシーとボンジュ〜ルだけしか知らない)
    • 大学周辺とか、ホテルや観光地の近くはもちろん英語通じるけどね。
    • EPFLの博士課程の学生と話したけどその人はフランス語1ミリもわからないまま生活してるらしい。
  • ホテルは高かったのでAirbnbで良さそうな家を1週間借りた。(それでも一泊8000円くらい)
  • 確かにレストランとかは激高なんだけど、スーパーの果物・パン・チーズは激安で超うまい。
    • 服とかの買い物は高いので、レマン湖をフェリーで1時間のところにあるevian(フランス)に渡ってそっちで買い物する人が多いらしい。
  • 水は蛇口をひねるとアルプス(?)の水がじゃぶじゃぶ流れてくるのでめちゃくちゃ美味しい。

f:id:tanishiking24:20200819131723j:plain
Lausanne 駅から(裏口に)出たすぐの写真、綺麗すぎて一瞬で来てよかったと思えた

続きを読む

プレーンな Aho-Corasick を実装したけどそんなに早くなかった

TL;DR

  • TypeScript で Aho-Corasick を実装しました https://github.com/tanishiking/aho-corasick-js
  • ベンチマークとってみたところだいたいのケースで普通のTrieで複数パターンマッチするほうが早かった https://github.com/tanishiking/aho-corasick-benchmark
  • (プレーンな) Aho-Corasick は Trie の最悪時間計算量を緩和するものの、実際の検索では failure state への遷移が非常に多く、ループの数は減らせるものの時間がかかってしまう(推測)
  • 状態遷移表(やダブル配列)を使ったより高速な Aho-Corasick の実装や、Boyer-Moore と Aho-Corasick が合体した Commentz-Walter とか、より高速に複数パターンの検索を実装する方法はある

参考


Aho-Corasick on TypeScript

JSで複数の文字列パターンを高速に検索したいことがあり、有名かつ実装が割と簡単という Aho-Corasick を TypeScript で実装してみた(ナイーブに実装されたものはいくつかnpmに公開されているが、しっかりメンテされていて安心して使えそうなものはなかった)。

github.com www.npmjs.com

(実装してみたと言っても GitHub - robert-bor/aho-corasick: Java implementation of the Aho-Corasick algorithm for efficient string matching による良く出来た Java 実装を部分的に TypeScript に移植しただけなのだが...)


ベンチマーク環境


ベンチマーク結果

いくつかのパターンに対してベンチマークをかけてみた。

自然言語っぽい sentence に対するベンチマーク

  • chance.sentence によって生成した sentence が検索対象
    • ちなみにsentenceにより生成されるwordはそれぞれ平均5,6文字だそう。
  • chance.word で生成した単語(?)郡がキーワード

長さ10000単語のsentenceに対して 10000キーワードを検索

❯ node index.js -k sentence --keywords 10000 --length 10000             
indexOf loop x 2.99 ops/sec ±1.81% (12 runs sampled)
simple trie x 111 ops/sec ±2.07% (67 runs sampled)
aho corasick x 33.11 ops/sec ±2.60% (54 runs sampled)

長さ10000単語のsentenceに対して 500000キーワードを検索

❯ node index.js -k sentence --keywords 500000 --length 10000             
indexOf loop x 0.04 ops/sec ±34.98% (5 runs sampled)
simple trie x 25.42 ops/sec ±14.62% (47 runs sampled)
aho corasick x 11.85 ops/sec ±17.90% (27 runs sampled)

長さ10000単語のsentenceに対して 100キーワードを検索

❯ node index.js -k sentence --keywords 100 --length 10000
indexOf loop x 347 ops/sec ±1.53% (82 runs sampled)
simple trie x 224 ops/sec ±1.03% (82 runs sampled)
aho corasick x 94.35 ops/sec ±2.48% (67 runs sampled)
  • キーワード数が少ないと単純な indexOf のループが一番早い(それはそう)
  • Aho-Corasick は総じて Trie に負けてる

機械的な文字列集合(DNA配列とか)(文字種少なめ) に対するベンチマーク

文字種が少なくキーワードが多い場合は Aho-Corasick の failure 遷移が よく効いてくるのではないかと思い、Aho-Corasick に有利な条件のベンチマークを設定してみた。

長さ1000の文字列に対して、10000キーワードで検索

❯ node index.js -k string --keywords 10000 --length 1000 --pool abcdefg 
indexOf loop x 54.19 ops/sec ±15.41% (45 runs sampled)
simple trie x 2,825 ops/sec ±9.39% (75 runs sampled)
aho corasick x 4,001 ops/sec ±3.11% (74 runs sampled)

長さ10000の文字列に対して、10000キーワードで検索

❯ node index.js -k string --keywords 10000 --length 10000 --pool abcdef
indexOf loop x 6.46 ops/sec ±28.38% (20 runs sampled)
simple trie x 305 ops/sec ±2.82% (72 runs sampled)
aho corasick x 223 ops/sec ±16.66% (62 runs sampled)
  • やはり文字種の少ない環境かつ検索キーワードが多い環境だと failure 遷移が効いてくる(一方で Trie は無駄な遷移が目立ってくる)のか、Aho-Corasick に軍配が上がった
  • しかし検索対象が長くなると Trie に逆転される...

プレーンな Aho-Corasick が Trie よりも遅い理由の推測

Aho and Corasick [AC75] presented a linear-time algorithm for this problem, based on an automata approach. This algorithm serves as the basis for the UNIX tool fgrep. A linear-time algorithm is optimal in the worst case, but as the regular string-searching algorithm by Boyer and Moore [BM77] demonstrated, it is possible to actually skip a large portion of the text while searching, leading to faster than linear algorithms in the average case https://www.researchgate.net/publication/2566530_A_Fast_Algorithm_For_Multi-Pattern_Searching

  • とあるように、Aho-Corasick は Trie による検索の最悪ケース (最長パターンのギリギリまで遷移して、受理状態に到達するギリギリで遷移に失敗するのを繰り返す場合 abcdefg というキーワードを abcdef という文字列から検索するみたいな) の対策にはなっていそうではある。
  • しかし実際の文字列検索では、確かに Trie は検索対象の文書に対して二重ループを走らせる必要があるものの、内側のループ(Trieで構築されたオートマトン上を遷移する過程)ではだいたいの場合、早々に遷移に失敗するはず
    • そのため最悪ケースでは二重ループを繰り返すものの、実際はそんなに時間かからないのではないか。
  • 一方 Aho-Corasick は二重ループを取り除き、一回の検索対象文字列に対する走査ですべての検索キーワードを見つけることができるが、遷移に失敗した場合に(failure遷移先から)遷移に成功するまでfailure遷移を複数回繰り返すことになる。
    • そのため検索対象文字列と、検索するキーワードがよく似ており、遷移が成功しやすい環境では failure 遷移による高速化がよく効くものの、遷移が失敗しやすい環境では(複数の) failure 遷移が多くなり、それによる検索速度の劣化が著しいのではないか。

このような原因でプレーンな Aho-Corasick 実装が Trie 実装に負けたのではないかと考えています(もしかすると単純に僕の実装がミスってて不要な状態遷移とかが起きてるのかもしれないが...何度読みなおしてもそのようなことにはなってないように見える)。

詳しいかたいらっしゃったら教えて下さい or 読むべき本/論文などあれば教えて下さい...。


さらに高速な複数文字列パターン検索のために

Aho-Corasick とは別の(亜種の)アルゴリズム

例えば GNU grep には Commentz-Walter algorthm という Aho-Corasick と Boyer-Moore が合体したようなアルゴリズムが使われていたり、いろいろ考えられいる。より高みを目指すならこれらのアルゴリズムを実装したほうが良いのだろう。

こういう文字列アルゴリズムをいっきに学べる良い本ないかなと思っていたら Flexible Pattern Matching Strings: Practical On-Line Search Algorithms for Texts and Biological Sequences をおすすめされたので買いました。

ところで、これらのアルゴリズムは割りと歴史が長く、モダンなCPUが開発された現代に最適な複数文字列検索アルゴリズムはないのだろうかと思っていたらあった。

Intelの開発している Hyperscan という複数文字列検索のための正規表現エンジンが存在し、ripgrep も SIMD が有効な場合は hyperscan の考えをベースに実装した Teddy というライブラリを使って高速な複数文字列検索を行うらしい。

REGEX SET SCANNING WITH HYPERSCAN AND RE2::SET hyperscan 早そう

Aho-Corasick の高速化

では Aho-Corasick はもうpracticalには役に立たないのかというとそんなことはなく、Aho-Corasick にはまだ高速化の余地があるようです。

例えば、今回実装したプレーンなAho-Corasickでは状態遷移部分をHashMapを使って実装していたが

We do still have a few tricks up our sleeve though. For example, many Aho-Corasick implementations are built as-if they were tries with back-pointers for their failure transitions. We can actually do better than that. We can compile all of its failure transitions into a DFA with a transition table contiguous in memory. This means that every byte of input corresponds to a single lookup in the transition table to find the next state. We never have to waste time chasing pointers or walking more than one failure transition for any byte in the search text. https://blog.burntsushi.net/ripgrep/


暇があれば Aho-Corasick の高速化や Commentz-Walter の実装と比較。また hyperscan について調べたりしてみたいな。 俺たちの高速文字列検索はまだまだこれからだ!

MtG Arena で学ぶ英語 - mono black aggro?

一年くらい前からMtG Arenaで遊んでいます、今は日本語UIあるのだけれど、いつ海外でデュエリストに襲われても意思疎通ができるように英語UIで英語のカード名に慣れようと、英語UIで遊び続けています。MtG、意外と知らない英単語たくさん出てくるので勉強になる。

ところでこのデッキを見てくれ、こいつをどう思う?

mtgarena.pro

低マナ帯とクリーチャー除去とで固めたデッキで、クリーチャー主体のデッキに対しては結構勝てる。知らない英単語結構見当たるので調べていこう。

Disfigure

gatherer.wizards.com

日本語: 見栄え損ない

Gutterbones

gatherer.wizards.com

日本語: どぶ骨

Knight of Ebon Legion

gatherer.wizards.com

日本語: 漆黒軍の騎士

Footlight fiend

gatherer.wizards.com

日本語: 脚光の悪鬼

Lazotep Reaver

gatherer.wizards.com

日本語: ラゾテプの肉裂き

  • reave
    • 強奪する/盗む
    • Urban Dictionary: Reave
    • 検索ヒット数とか、cambridge 辞書にないのを見るとあまり使われない言葉なのかも

Orzhov Enforcer

gatherer.wizards.com

日本語: オルゾフの処罰者

Priest of Forgotten God

gatherer.wizards.com

日本語: 忘れられた神々の僧侶

特に難しい言葉はないけどpriestの関連語彙

  • priest: 僧侶
  • monk: 僧
  • cleric: 聖職者
  • father: 神父
  • pastor: 牧師

Midnight Reaper

gatherer.wizards.com

日本語: 真夜中の死神

  • reaper
    • reapは元々"刈る"という意味
    • 転じて今では reaper で死神という意味になってそう(reaperで画像検索すると死神の画像しか出てこない)
    • 死神 / 殺し屋
    • Grim reaper: 死神

Drag to the Underworld

gatherer.wizards.com

日本語: 死の国への引き込み

Rankle, Master of Pranks

gatherer.wizards.com

日本語: 悪ふざけの名人、ランクル

prank video でドッキリビデオの意味になる: こういうの↓

理想の家族〜

Spawn of Mayhem

gatherer.wizards.com

日本語: 騒乱の落とし子

京都の観光地の大混雑具合に対して it was mayhem って言ってるのも聞いたことある。