• AUGraphがSwiftで使えない問題

    AUGraphを使っているとコールバック関数を登録しなければならないことがあります。

    [c]

    AURenderCallbackStruct callbackStruct;

    {

    callbackStruct.inputProc = recorderCallbackFunction;

    callbackStruct.inputProcRefCon = reinterpret_cast<void *>((__bridge void *)self);

    }

    err = AUGraphSetNodeInputCallback(mAuGraph, remoteIONode, 0, &callbackStruct);

    [/c]

    こういった形で。

    SwiftでAUGraphを使う場合1つ問題があり、これには関数のポインタが使われているのですがこれがSwiftで使えません。上で言うとrecorderCallbackFunctionの部分です。
    つまり

    [c]

    func callbackFunction(

    inRefCon:UnsafeMutablePointer<Void>,

    ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,

    inTimeStamp:UnsafePointer<AudioTimeStamp>,

    inBusNumber:UInt32,

    inNumberFrames:UInt32,

    ioData:UnsafeMutablePointer<AudioBufferList>) -> (OSStatus)

    {

    return noErr;

    }

    [/c]

    こういうcallbackを用意したとして、

    [c]

    var callbackSgtruct = AURenderCallbackStruct(inputProc: callbackFunction , inputProcRefCon: nil)

    error = AUGraphSetNodeInputCallback(mAuGraph, remoteIONode, 0, &callbackSgtruct)

    [/c]

    としてもエラーで登録できないのです。
    この問題は少なくともSwift1.2まで存在しています。
    解決法は現状は存在しないようなので、この部分に関してはObjective-Cを使うしか無いようです。

  • 3端子レギュレーターが劣化した

    半年ぐらいRaspberryPiをdriveしてた3端子レギュレーターが劣化した。
    壊れたわけじゃなくて、特性が悪くなった。
    供給元の電源電圧のパルス状の低下(モーターが回るみたいなね)が出力に現れるようになって、なんかあるとすぐにRaspberryPiがshutdownするようになった。
    半導体ってこんなふうに劣化するんだね、いい勉強になった。

  • RaspberryPi Door Locker

    This is a english edition of RaspberryPiとサーボモーターで家のドアをiPhoneから開ける

    I needed a system which unlock the door from remote.

    2014-08-13 21.49.34

    This is a instruction.

    Structure

    First, The regulator convert 6V to 5V and supply it to raspberry by USB. And 6V supplied to servomotor directly. signal line is connected servo motor to GPIO on raspberryPi. Also, RaspberryPi is online by LAN.

    名称未設定アートワーク-8

    Software

    It is simple. just controlling the PWM.
    But, I wanted to control it from my browser on iPhone. So, I made CWeb( Web framework for C lang ). By using it, It receives HTTP requests and decide to unlock and lock it. So, To unlock it, I just need a Browser. All source codes are on a github account. Easy to deploy. Just enter RaspbeeryPi and git pull. I have no static global IP, So I prepared dynamic DNS.

    Hardware

    This servo motor can rotate 360 degree. It is possible that power supply is down. So This system never block unlock by a key. I figure out it. This photo shows my solution.2014-08-14 22.26.30 のコピー

    270 degree rotations is required to the servo motor.
    2014-08-14 22.26.30

    I 3d printed parts. Of course  just fit.

    2014-08-07 23.26.32
    2014-08-07 23.27.13
    2014-08-12 23.56.52

     

  • 誤差逆伝搬法その3

    前回ので1つのニューラルネットワークのニューロン(ノードと言ったりする)を学習させる方法をやりました。今回はそれらをつないだ本当のネットワークの話です。
    1つ1つは同じですから、基本的には前回やったことをすべてのニューロンに対して行うだけです。

    3入力3出力

    今回学習させるのはこんな3入力3出力で3層のネットワークです。ただ、ニューラルなのは最後の2層だけで、1層目は入力された値をそのまま出力するだけです。

    Screen Shot 2014-12-02 at 20.30.26

    2層目は受け取ったそれぞれの入力を元に出力します。線が多いので見にくいのですが、中央の入出力だけに注目するとこのようになっています。真ん中のレイヤーは入力それぞれに重み付けがあることになりますね。出力は3本に分かれますが、どれも同じ値になります。

    Screen Shot 2014-12-02 at 20.32.38

    3層目も同じく3つの入力がありますが、出力が1つだけで、これが最終の出力となります。3入力3出力のネットワークですから例えば入力がx(0.2, 0,1, 0.9)に基づいてy(0.8, 0.7, 0.7)といった出力になるわけです。これの学習をさせてみましょう。

    最後の層の学習

    前回どうやって学習させたか思い出してみましょう。
    学習は「出力と教師データにズレがなければしなくていい」ものですよね。つまり、「出力と教師データがズレてるから調整しないといけない」わけです。
    実際のズレを元にシグモイド関数の微分を使って、例えば3だけ無駄に大きいってときに「入力をどんだけ変えたら出力を3下げられるか」を計算して、そこから重み付けを変更したわけです。
    ということはパラメーターの調整には”ズレ”と”そのパラメーターを変えるとどれだけ誤差が小さくなるか”の2つが必要なわけです。

    出力に近い側から調整していきます。
    何故かと言うと値を変えたらどのぐらい誤差が小さくなるかを最も計算しやすいからです。
    y1を見てみましょう。

    Screen Shot 2014-12-02 at 21.11.03

    y1 -> E
    まず、y1はどれぐらいズレてるんでしょうか。その誤差ってどれぐらい全体の誤差に影響してるんでしょうか。全体の誤差は教師データを大文字のYで表すとしたら

    E = (y_1 - Y_1)^2 + (y_2 - Y_2)^2 + (y_3 - Y_3)^2

    こうなりますよね。y1はy2やy3に影響しないってのがよくわかります。
    じゃあy1が1変わると誤差はどんだけ変わるのかというと

    \frac{\partial E}{\partial y_1}=2 y_1 (y_1 - Y_1)

    こんな風に簡単になります。
    試してみましょう。y1=1でY1=0のときは1多いわけですがその時は。

    \frac{\partial E}{\partial y_1}=2 \cdot 1 (1 - 0) = 2

    ということになります。これはつまり「yをあと1増やすと誤差が2増える」という意味ですね。
    実際はy1=1の誤差は1で、y1=2の誤差は4ですから(誤差は2乗だからね)予想よりも誤差は増えてますがそれは二次曲線で接線の傾きはどんどんきつくなるからですね。

    とにかく、これでy1が1変わると誤差がどのぐらい変わるのかが分かりました。
    誤差がわかればy1を調整可能です。
    y1を操作しているのはs1ですから、今度はs1を1増やしたらy1がどんぐらい増えるのかを知る必要があります。それがわかれば逆にs1のあるべき値がわかりますね。

    s1 -> y1
    それ考えるのはすごく簡単です。ただのシグモイド関数ですから

    \frac{\partial y_1}{\partial s_1} = sigmoid'(s_1)

    \frac{\partial y_1}{\partial s_1} = \alpha sigmoid(s_1) (1 - sigmoid(s_1))

    となります。

    wn -> s1
    ここまでは簡単でした。ここからも簡単です。
    とうとうs1を1変えたらy1がどれだけ変わるかが分かったので、s1の変化で誤差がどれだけ変わるかを計算できるようになりました。では、s1を変えるにはどうしたらいいかってところでとうとう重み付けが出てきます。
    s1ってのはそもそも、ただ入力を合計しただけのものです。

    s_1 = w_{0}x_{0} + w_{1}x_{1} + w_{2}x_{2} + w_{3}x_{3}

    s_1 = {\displaystyle \sum_{i=0}^{3}} w_{i}x_{i}

    x0とw0ってのは、前回やったやつで、x0は常に1です。
    さて、じゃあwnが1増えるとs1がどれだけ増えるのかを考えてみましょう。

    s_1 = w_{0}x_{0} + w_{1}x_{1} + w_{2}x_{2} + w_{3}x_{3}

    \frac{\partial s_1}{\partial w_n}= x_{n}

    wnを1増やすとs1がxnだけ増えるという話です。
    考えてみれば当たり前な話で、元々 x*wですからx*(w+1)にしたら増えるのはxです。
    xが0だと0になります。これは「入力がないんだからwを何にしてもs1に影響なんかないよ」
    ってことです。

    合体!
    ここまででEに与えるy1の影響、y1に与えるs1の影響、s1に与えるwnの影響を計算しました。
    ここまでわかると「Eを1減らすにはwnをどれだけ減らせばいいか」がわかります。それは

    \frac{\partial E}{\partial w_n}

    これですが、それって

    \frac{\partial E}{\partial w_n} = \frac{\partial E}{\partial y_1} \frac{\partial y_1}{\partial s_1} \frac{\partial s_1}{\partial w_n}

    こういうことです。これは今まで計算してきた3つのものですから。

    \frac{\partial E}{\partial w_n} = 2 y_1(y_1 - Y_1) \cdot \alpha sigmoid(s_1)( 1 - sigmoid(s_1) ) \cdot x_{n}

    ということになります。3つのものそれぞれに色を付けてみました。

    Screen_Shot_2015-01-30_at_18_18_20

    完成したこの式は見るとややこしいですが、1個ずつ見ていけば大したことありませんでしたね。
    この式を計算するとある数値が計算されます(xnとy1とs1は既に一回ネットワーク動かせば出てくる値で、Y1は教師データですから)。その値ってのは「wnを1増やすと誤差がどれだけ”増えるか”」という値です。3だったなら1増やすと誤差が3増えるわけです。

    この値を使ってwnを調整するには

    w_n(new) = w_n(old) - \varepsilon \frac{\partial E}{\partial w_n}

    と、イプシロンを使ってちょっとずつ動かしていけばいいことになります。
    これで最後の層の入力側は全部調整できます。

    1つ前の層の学習

    では、その1つ前はどうやりましょう。

    Screen Shot 2014-12-02 at 22.08.54

     

    結局のところ「誤差にどれだけ影響するか」 ってことですよね。
    では、上の図のs5っていうニューロンに注目してみましょう。
    前回、wnの誤差への影響 を調べましたが、それを簡単にxnの誤差への影響に変えられます。

    s_1 = w_{0}x_{0} + w_{1}x_{1} + w_{2}x_{2} + w_{3}x_{3}

    \frac{\partial s_1}{\partial w_n}= x_{n}

    でしたから、これをxnにすると

    \frac{\partial s_1}{\partial x_n}= w_{n}

    ということになります。ということはさっきの式をちょっと変えるとこのs5の出力が1変わると誤差がどれだけ変わるのかがわかります。

    \frac{\partial E_1}{\partial y_5} = \frac{\partial E_1}{\partial y_1} \frac{\partial y_1}{\partial s_1} \frac{\partial s_1}{\partial x_n}

    \frac{\partial E_1}{\partial y_5} = \frac{\partial E_1}{\partial y_1} \frac{\partial y_1}{\partial s_1} w_1

    ただし、これは出力y1だけの話です。y2とy3にも影響を及ぼしてしまいます。

    \frac{\partial E_2}{\partial y_5} = \frac{\partial E_2}{\partial y_2} \frac{\partial y_2}{\partial s_2} w_2

    \frac{\partial E_3}{\partial y_5} = \frac{\partial E_2}{\partial y_1} \frac{\partial y_3}{\partial s_3} w_3

    この誤差E1~E3というのは足すとEになります。独立していますからね。
    ということは全部合わせた「s5の出力y5が誤差に与える影響」ってのは

    \frac{\partial E}{\partial y_5} = \frac{\partial E_1}{\partial y_1} \frac{\partial y_1}{\partial s_1} w_1 + \frac{\partial E_2}{\partial y_2} \frac{\partial y_2}{\partial s_2} w_2 + \frac{\partial E_2}{\partial y_1} \frac{\partial y_3}{\partial s_3} w_3

    こうなります。少し見やすくすると
    \frac{\partial E}{\partial y_5} = \frac{\partial E_1}{\partial s_1} w_1 + \frac{\partial E_2}{\partial s_2} w_2 + \frac{\partial E_3}{\partial s_3} w_3

    これは計算可能ですね。しかも前回計算したはずの値が沢山入っています。
    これでy5をどういう風に調整したら誤差が大きくなるか・小さくなるかがわかります。
    ここまでくればさっきやったのと全く同じなんです。いま”出力をどうしたら良いか”まではわかってるんですから。

    s5のy5への影響は

    \frac{\partial y_5}{\partial s_5} = \alpha sigmoid(s_5) (1 - sigmoid(s_5))

    です。
    s5につながってる入力側の重み付けwnがs5に与える影響は

    \frac{\partial s_5}{\partial w_n}= x_{n}

    ですよね。
    今までのを合計するとこうなります。

    \frac{\partial E}{\partial w_n} = \frac{\partial E}{\partial y_5} \frac{\partial y_5}{\partial s_5} \frac{\partial s_5}{\partial w_n}

    \frac{\partial E}{\partial w_n} = (\frac{\partial E_1}{\partial s_1} w_1 + \frac{\partial E_2}{\partial s_2} w_2 + \frac{\partial E_3}{\partial s_3} w_3) \cdot \alpha sigmoid(s_5)( 1 - sigmoid(s_5) \cdot w_{n}

    これは何かというとs5の入力側のwnが全体の誤差に与える影響です。
    あと何をすべきかはわかりますね。これとイプシロンを使ってwnを調整します。
    これを2層目全体に行えば2層目は終わりです

    伝搬していく

    誤差逆伝搬法をコンピューターで計算する時に便利なのは

    \frac{\partial E}{\partial w_n} = (\frac{\partial E_1}{\partial s_1} w_1 + \frac{\partial E_2}{\partial s_2} w_2 + \frac{\partial E_3}{\partial s_3} w_3) \cdot \alpha sigmoid(s_5)( 1 - sigmoid(s_5) \cdot w_{n}

    における

    \frac{\partial E_n}{\partial s_n}

    が既に計算済みであるという点です。それを使って自分の出力が誤差に与える影響を調べて入力側の重み付けを調整していくのです。

    仮にこれの前にもう1層あったらどうしましょう。
    もうみなさんなら出来るはずです。層がどんなにあったとしても次の層が誤差に与える影響ってのはその層の誤差を調整した時に計算するからです。

    前へ 人工ニューラルネットワークへ戻る

  • 卒業論文で何やってたか

    時々卒料論文で内出血の研究をしてた話をすると興味を持ってくれる人がいてちょっとおもしろい。いつも内容を説明するのがちょっと難しいので一回文章にしてみることにしました。もちろん論文はあるけどやたら長いのでどんなことしてたのかってわかりやすくね。

    タイトル
    「低輝度集合解析を用いた臓器境界判定の自動化による内出血貯留検出手法の構築」

    内出血?

    外から血がドバドバ出てれば「こいつやばい」って分かるんだけど、中でこっそりちょこっとずつ出血されると知らないうちに死んじゃうことがあるのが怖い内出血。
    救急車で搬送されると超音波で体内をチェックするんだけど、残念ながら内出血してる人のうち約57%はこのチェックをスルーしちゃう(Bala Natarajan; FAST scan:Is it worth doing in hemodynamically stable trauma patients?, 2011 Surgery)
    とゆーことでもっと超音波で内出血見つけたい -> 画像解析したらいいんじゃないかってのが話の始まり。

    超音波画像

    超音波画像ってのはこんなやつ

    6_Image57

    注釈付けてみました

    6_Image57

    これを見るとわかりますが血管は黒いんですね。中の血液が超音波画像だと黒くなるからこうなるんです。で、内出血するとこれが臓器と臓器の間にはいってこういうところに黒い影が出来るわけです

    6_Image57 2

    もちろん僕は健康なので内出血なんかしてませんが、するとここに溜まってきます。(体内で血が溜まる場所は実は決まってます。詳しくはFASTで検索)

    どう画像解析するか

    黒いものを探せばいいんですが、そんなの画像中のどこにでもあります。臓器の間じゃないとダメなんです。そこで

    1. ただ黒いんじゃなく、明るい場所に囲まれた黒いエリアを探す
    2. 臓器を識別する

    をやることにしました

    黒い場所を探す

    1pxだけ黒いとか、段階的に暗くなるとかじゃなくて、ある部分まで明るかったんだけど急に暗いっていうグランドキャニオンみたいになってる場所を探すのがやりたいことです。血管も中は黒いけど周りは白いもんね。基本的には微分を使うんですが、1pxの隣接する8方向との微分値を使ってさらに隣との、、、ってやるとこんな風になります。

    detectBlood_hitlevel5_oya2

    あんまりいいデータ残ってないのでこんななのですが、こういう感じになります。赤いのが「輝度変化の激しい境界に囲まれている輝度の低い部分」です。右下にある血管は見事に選ばれてますね。

    臓器を識別する

    上のやつだけだと血管がパーフェクトに見つかってしまうので、見つけたい内出血とは違います。臓器を識別して血管じゃない部分を探さないといけないです。
    内出血が溜まる場所として腎臓と肝臓の間のモリソン窩をターゲットにしてますので、腎臓と肝臓がどこにあるのかが分かって、その間ってのがどこなのかがわかればいい。
    で、腎臓と肝臓はそれぞれ輝度の極値調査をするとScreen Shot 2015-01-28 at 23.41.40

    こういう若干気持ち悪い画像になるのですが、この白黒の出現率やパターンが違うのに気づいたのでこれを使おうと(違う理由は多分水分や鉄分の含有量だと思いますが、興味無かったので研究してないです。誰かやりたいひとどうぞ)

    それとは別に境界線の情報も欲しかったのでまずmeanshiftします

    Screen Shot 2015-01-28 at 23.44.25

    そしてこれの微分画像を求めます

    Screen Shot 2015-01-28 at 23.44.33

    これでいいですね。これとさっきの気持ち悪い画像を使って肝臓を探していきます。

    まず、ここは肝臓だろうという場所に最初の肝臓を描画します。
    その位置ってのは画像解析じゃなくて画像の右上どこどこっていう指定方法なので、超音波プローブが違うところにあってもここに確定されます。(このへんはどうにかしたい)

    Screen Shot 2015-01-28 at 23.47.04

    上の赤いのがそうです。早速上すぎでずれてますが気にしない。緑はさっきのmeanshiftして微分した画像の値を緑で投影したものです。つまり強い境界線などを示しています。
    あとはこの赤いのを広げていきます。コンピューターの生物シュミレーションと同じです。展開していきます。

    Screen Shot 2015-01-28 at 23.46.17

    こうやってね。
    ただ、止まる要因が必要ですよね。それはこうします

    1. 気持ち悪い画像から計算した値が肝臓っぽくなくなってきたらその周辺が止まる
    2. 境界にぶち当たったらその周辺が止まる

    それぞれのピクセルがライフカウントを持っています。境界にぶつかったりすると周辺のピクセルのライフカウント値のカウントダウンがはじまり0になると止まる仕掛けです。
    Windowsのペイントで”塗りつぶす”をやるとちょっとでも隙間があると色がもれますが、もれないようにする っていうと分かりやすいかも

    Screen Shot 2015-01-28 at 23.46.27

    最終的にはこの辺を肝臓だと判断しました。およそ合ってます。
    左や上の方は境界線で、右下は”肝臓っぽくないぞ”ってことで止まっている印象があります(まぁデータ見ないとわからないんですがね)
    同じことを腎臓にもやるとこうなります。腎臓は青で色を付けてあります。
    腎臓は内部の変な組織のせいでドーナツになってますね。

    detectSeparation_oya2

    ここまで来ると何がわかったかというと腎臓と肝臓の位置が画像の中でわかったので、
    どの辺が腎臓と肝臓の”間”なのかが分かります。

    ここまでくれば簡単ですね。あとはこの”間”に上のほうで求めた”黒い場所”ってのがあるのかどうかを調べればOKです。

    研究だと

    研究では実際に内出血画像のデータを病院にもらいにいってそれと友達の健康なエコー画像ももらって両方解析して「ほらね、精度いいでしょ」ってやって終わりでした。

    当然もっとよく出来るはずで、前提条件もそれなりに多いですし。
    それこそここで紹介しているのはうまく動いている場合の画像です。うまくいかないのも当然あります。この時はpapelookの法人化もしてたし、住んでた彼女の家が学校まで遠くて全然行ってなかったのですが、今思えば面白い研究だからもうちょっと取り組んでも。。まぁ時間あったら改善してオープンソースにしてみたいですね。