• nRF51822のS110 v7.1を使うプロジェクトを作る(CMSIS)

    nRF51822はBLEの利用できるNordicのチップですが、ソフトデバイスと呼ばれるNordicの用意しているプログラムを利用することで簡単にチップからBLEのadvertisingを出せたりします。

    そのソフトデバイスにはいくつか種類がありますが、違いはCentralになるかPeripheralになるかです。
    S110はPeripheral用のソフトデバイスです。(CentralはS120で、両方ってのがS130)

    S110自体はNordicのサイトからhexファイルでダウンロードできます。
    また、KeilのCMSISにS110を使うためのもろもろが用意されているので今回はCMSISを使ってプロジェクトを作り、とりあえずAppleのiBeaconと同じパケットを出して見るところまでやってみます。

    Keilを開く

    ではプロジェクトを作りましょう。
    ターゲットを nRF51822xxAAでプロジェクト作成します。
    CMSISにS110を追加。まずCMSIS管理画面で
    nRF_SoftDevice, nRF_Libraries, nRF_Drivers, nRF_BLE, それにnRF51822のデバイスのものをInstallします。それから、このプロジェクトにS110を追加し、関係するファイルも追加します(Resolveを押すと関係したファイルを一斉に追加してくれます)
    終わるとこんなかんじになるはず
    F60A5EB1-9C5E-4287-BB92-0F2A55391F9D

    あとはプロジェクト設定です。S110とフラッシュに同居するのでROMやRAMの設定が必要です。

    プロジェクト設定にて
    Xtalを16
    ROMを0x16000 – 0x29000
    RAMを0x20002000 - 0x6000
    UseMicroLibにチェック

    プロジェクト設定 C/C++
    Defineに
    NRF51 BLE_STACK_SUPPORT_REQD S110 SOFTDEVICE_PRESENT
    MiscControls に
    –c99
    ASMにも同じdefine

    プロジェクト設定 Linker
    Use memory layout from target dialog

    プロジェクト設定 Debug
    DebuggerにJLINKを指定。

    DebugSetting
    SettingでPortをJTAGからSWにする
    FlashDownloadのRAMは
    0x20000000 – 0x2000
    ResetAndRunにチェック入れると便利

     

    プログラム

    プログラムは
    https://github.com/yuki-sato/nRF51822-S110-v7-iBeacon/
    に用意しました。
    NordicのS110の別のチップ用だったサンプルを流用したものです。
    main.c以外にめんどくさがりな貴方のために、プロジェクトやCMSISもそのまま置いてありますから、これをCloneしてきたらすぐにフラッシュ書き込みしてiBeacon出せるはず。

    ちなみにライターは勝手にJLinkでマイコンとつないでることにしてますが、そういう人多いはず。
    その場合はこれで問題なくフラッシュ書き込みできるはず。
    S110を書き込むのは忘れないように。nRFGoで書き込んでくださいね。

    S110を書き込むときの注意

    S110を書き込むときにはまず
    最新のnRFGoを使う
    ことを忘れないで下さい。そうじゃないとv7のS110は書き込めません
    あと、まだ詳しく調べられていないのですが、protectionのチェックをはずさないと途中でエラーで割り込まれてしまいます。エラー割り込みの引数
    uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name
    それぞれ 2 0 0 となる感じで。同じ症状の人はprotectionを外しましょう。

    8543A411-27FF-4EBB-AB33-444767B88044

  • 誤差逆伝搬法その2

    ここに来てやっとですが、人工ニューラルネットワークを学習させてみましょう。
    前回はある関数をある教師データに近づけるために(教師データとの誤差を最小にするために)頑張ってました。やることは人工ニューラルネットワークでも同じことです。ではニューロン1つを見てみましょう。

    Untitled_Artwork-10

    すごく久々なので忘れちゃった人がいるかもしれません。思い出してみましょう。
    これはNこの入力を持つニューロンで、それぞれ入力値がx1からxNです。ただ、入力は単純には足し算されずに、それぞれ重み付けw1〜wNが掛け算されます。それが全て足されてsigmoid関数の入力となり閾値θ合わされて出力値yが決まります。
    式で書くとこんなでしたね。

    y=sigmoid\left({\displaystyle \sum_{i=1}^{N}} w_{i}x_{i} - \theta \right)

    では、この1つのニューロンを学習させましょう。学習というか、変更できるニューロンの値というのは重み付けのw1〜wNと閾値θです。

    Untitled_Artwork-12

    つまりこれを教育するっていうことは
    「あるx1x2…xNを入れた時に欲しいyが出るようなw1w2…wN」にするってことです。例えばx1にだけよく反応するようにしたければw1だけ大きなものにしたいですし、x1でもx2がきても大きなyにしたければ閾値θが小さくなるか、w1〜wNの全部が大きくなるといいですね。どっちにするか悩みどころですね。というか閾値θだけ邪魔ですね。他は綺麗に並んでるのに。なんとか重み付けであるwと同じようにシータも扱えないでしょうか。

    閾値シータを重み付けに混ぜる

    今閾値θはこんなふうに使われています

    sigmoid(  (x1・w1 + x2・w2 〜 xN・wN)  –   θ  )

    ここでθをw0として扱いたいのですが(何故そうしたいかはあとで分かります)。
    こうするのはどうでしょう。

    Untitled_Artwork-13

    いつも1であるx0を用意しちゃってw0を用意してみました、すると

    sigmoid(  (x1・w1 + x2・w2 〜 xN・wN)  –   θ  )

    をx0を絶対1の入力、w0を-θとして後でθを計算するとすると

    sigmoid(  x0・w0 +   x1・w1 + x2・w2 〜 xN・wN   )

    こうやって全部xとwの掛け算の形で表せられました。つまりこのニューロンは

    y=sigmoid\left({\displaystyle \sum_{i=0}^{N}} w_{i}x_{i} \right) (x0=1, w0=- \theta)

    ということで、学習というのはw0~wNの値を決める作業ということになります。
    これでちょっと簡単になった。

    評価関数

    では、これを学習させましょう。理想は「教師データと同じ結果を出すようなw0~wN」ということです。評価は教師データと実際に計算した値とのズレが小さいことです。だから、手元に教師データの入力値を入れて計算したy(i)と、その時の理想的な値であるY(i)を使うとその誤差は

    E(w0,w1,,,,wN) = (Y(i) – y(i))^2

    こうやって表せられます。(なんで2乗するかは前回やりました)
    評価関数Eはw0〜wNの関数になっています。
    当たり前ですね。w0だけいい感じに合わせても他がダメなら誤差だらけです。
    これを小さくしたいんですが、じゃあどうやってこれが小さくなるように調整すればいいんでしょう。「大きすぎるな」「小さすぎるな」って時に一体どのwをいじればいいんでしょうか。

    大きい物ほど小さく

    もちろんどのwの値をいじっても結果を修正することが出来ますが(xが0でなければね)、ただ選ぶことが出来ません。どうするかというと
    全部いじる
    のです。
    大事なことですが、結局誤差を表す評価関数を0にしたいわけじゃなくて小さく出来ればいいんです。別に算数みたいに一発で教師データに合うように調整する必要は全くないんです。
    ということで結果が大きかったならwを全体的に小さく。逆に小さかったらwを全体的に大きくすればいいんです。
    ただ、これだとwが全部同じ値になっちゃうので大きな入力があるやつほど大きく変えてしまうというやり方をします。

    イメージなんですが、結果が大きすぎるときに、例えばこんな感じでx1だけ他に比べて大きかったとします。

    Untitled_Artwork-15

     

    この時に、入力がでかいやつほど、その分重み付けwを小さくすることで調整します。
    こんなイメージ。

     

    Untitled_Artwork-14

     

    もちろん、他のw2やw3も小さくします。1つだけ選ぶということではありません。結果が大きいとわかった時には入力の値に応じてwを”全て”小さくします。ただ「うるさいやつほどボリュームを下げる」ということです。

    どのぐらい変えてやればいいのか

    大きいやつほど小さくしたいとして、単純にi番目の重み付けwを
    wi = wi – x*0.0001
    とかで小さくすれば入力であるxの大きさに応じてwiを小さくすることが出来ます。これもありです。これを全てのwに行うっていうのを沢山の教師データ使ってやる。どうせ、一発でwiを決めなくても良いのですから。1回ごとに正解に近い答えを出せるwiに近づけばそれでいいんです。0.0001を小さめにしてちょっとずつちょっとずつ動かせば安心ですよね。計算を何度もやることで目的のwiにすることが出来ます。

    ただそのやり方は「w0もw1も誤差に対する影響が同じぐらい」という前提に立っています。しかも「誤差が大きくても小さくても動かす大きさが一定」です。
    これだとよくないので、早く答えにたどり着くためにも誤差の大きさなども考慮しましょう。

    wiがどのぐらい誤差に影響するのか

    評価関数は

    E(w0,w1,,,,wN) = (Y(i) - y(i))^2

    でした、この関数はw0〜wNの全てに影響を受けます。じゃあi番目の教師データを使ってる時の誤差に対して、n番目の重み付けwn(さっきまでiでしたが、i番目の教師データっていうのでiを使っているのでnにします)がどのぐらい影響をおよぼすのか。誤差を表す評価関数をwnで偏微分してみましょう。

    \frac{\partial E_{(w0,w1,,,wN)}}{\partial wn} = -2(Y(i) - y(i))sigmoid'(s)Xn(i)

    そして、sigmoid関数は微分可能なので、微分しちゃいます(シグモイド関数 微分 とかでググってください)

    \frac{\partial E_{(w0,w1,,,wN)}}{\partial wn} = -2(Y(i) - y(i)) \alpha y(i)( 1 - y(i))Xn(i)

    このような形になります。ここでやっとニューロンにシグモイド関数を使った意味が出てきます。
    微分した式に出てくるのは微分する前の計算結果y(i)や理想的な値であるY(i)、それにi番目の教師データのn個目のXn(i)ぐらいなもんで、難しい計算がないのです。
    これだけでwnがi番目の教師データの時の誤差に与える影響(つまり偏微分値)が分かります。
    これが得られればこれを指標にしてwnを変更しましょう。勾配法的にやると

    wn = wn - \varepsilon \frac{\partial E}{\partial wn}

    と、wnをwnの偏微分値と小さな何かの値を使って更新します。(長かったのでE(w0,w1,,,wN)をEにまとめました)この偏微分値にさっきのを入れると。

    wn = wn - \varepsilon \cdot (-2)(Y(i) - y(i)) \alpha y(i)( 1 - y(i))Xn(i)

    となります。ところで、

    (-2)(Y(i) - y(i)) \alpha y(i)( 1 - y(i))

    の部分はi番目の教師データが決まれば決まる値でnが何であれ関係ありません。これをAとでもしておくと

    wn = wn - \varepsilon A \cdot Xn(i)

    となります。これは教師データからAを計算できればそれを元にwnをどのぐらい修正すればいいのかがわかります。
    ちょっとまとめにはいりますが、具体的にこれをつかってどうやって学習させるかというと

    1. w0~wNを適当に最初決める(これでx0~xNを入れると結果が計算できる)
    2. εを適当な小さな値の何かに決める。
    3. i番目の教師データを使ってAを計算する
    4. Aとεをつかってw0〜wNを全て修正する。

    これを繰り返すわけです。
    結局wnの誤差に対する影響度を調べるために偏微分とかの計算したものの、結局Aという固定な結果になって「w1だろうがwNだろうが影響度は同じ」ということになりました。そういう意味では
    wn = wn – xn(i)*0.0001
    とほぼ同じ結果です。0.0001がεAという値になっただけです。
    ただし、Aは教師データによって変わってきます。誤差が大きければそれだけAも大きくなるので単純に0.0001とするよりはより早く答えに近づきます。

    まとめ

    • 人工ニューラルネットワークの1素子も教師データで学習させられる
    • 閾値は1つの入力として捉える
    • 誤差を元に閾値を変えていくが、うるさいやつほどボリュームを下げる

     

     

     

  • 2. 速度と加速度

    基本的な話

    力学を勉強する上で速さの計算は大事です。確認としてちょっとした問題を出してみますね。時速100km/h で自動車が4時間走り続けたらどれだけ移動するでしょうか。400kmですね。大体沖縄一周ぐらいです(一周してみたいなぁ)。さて、速さをv、走った時間をtとした時その移動距離yは

    y = v ・ t

    こんな関係ですよね。速さ かける 時間 が移動距離です。逆に400kmを4時間で移動した車があるとすればその速度vはどうなるでしょう。式で書いてといてみると

    v= \frac{y}{t}=\frac{400}{4}=100(km/h)

    こんな感じになりました。そして、400km先までずっと100km/hで移動したのならその時間tは

    t= \frac{y}{v}=\frac{400}{100}=4(hour)

    となりました。簡単でした?

    y(移動距離) = v (速度) × t (移動した時間)

     加速度

    世の中にはだんだん速くなる動きがたsection0_04くさんあります。ちなみに物理の世界ではものが動くことを「ものが運動する」というので今後は”動く”とか”動き”の代わりに”運動”を使うことにしましょう。さて、だんだん速くなることを「加速する」って言いますよね。今回は加速する運動について考えます。さて、ここにだんだん速くなる箱を用意しました。同じペースで速くなるようで、こいつの運動を1秒ごとに観察するとこうなりました。この箱の加速の大きさ、つまり、加速度を考えてみましょう。

    始めは止まっていたので速度をvとしたとき、vは0です。そこからだんだん速くなって1秒後にv=2(m/s)の速さになりました。
    m/sは何かって? これは「メートル毎秒」です。時速が1時間に進む距離ならメートル毎秒というのは1秒に何メートル進むかです。2(m/s)なら1秒に2メートル進むような速さってことです。ちなみに2(m/s)で1時間、つまり3600秒進み続けると7200m進みますね。だから、1(m/s)という速さは時速で言うと7.2(km/h)です。早歩きぐらいのスピードですね。2(m/s)と7.2(km/h)は同じ速さってことです。なぜkm/hじゃなくm/sを使うかというと、消しゴムを放り投げるような物理の実験するときはkmや1時間よりもメートルや1秒2秒の方を使うほうが便利なので物理は普通m/sを使います。なので、今後はkm/hじゃなくてm/sを使っていきます。さてと、1秒後に2(m/s)になったこの箱ですが、2秒後にはどんな速度になるでしょうか。もしも、同じペースで速くなるなら2秒後はv=4(m/s)となります。では3秒後は?ここまで来ると関係に気づくと思います。1秒ごとに2(m/s)ずつ速くなっています。 このときの「1秒ごとにどのぐらいスピードが増えるのか」というのが加速度と呼ばれるものです。つまり1秒ごとに1(m/s)ずつ速くなるものは加速度が1。1秒ごとに2(m/s)ずつ速くなると加速度は2なわけです。どうやら加速度×加速した時間で速度を計算できそうです。英語で加速することをaccelerationというので、加速度をaとすると、t秒後の速度vはv = a・tとなりますね。

    加速度がaのときのt秒後の速度vは

    v = a ・ t

    単位の話

    速さはm/sとかkm/hという単位を使いました。では、加速度の単位は何でしょうか、速さに関係してるからm/s?? ってかそもそもm/sってどこから来たんでしょうね。速さの計算方法に戻ってみましょう。速さは「移動した距離をかかった時間で割る」と出て来ましたよね。

    v= \frac{x}{t}

    では、移動した”距離”の単位はメートルです。時間は秒です。では、その距離を時間で割った速さの単位は何でしょうか。ここがポイントです。単位も式と同じようにそのまま割ったものを使えばいいのです。だから、速さはこうなります

    \frac{m}{s}

    メートルのものを秒で割って速さを求めるわけですから速さの単位はメートル毎秒となります。m/s といつも書いてますが、これは分数を横にしただけです。逆に、速さかける時間は距離ですよね。これも、

    \frac{m}{s}\cdot s = m

    こんな感じでm/sにsをかけたら見事にmになりました。
    では、加速度の単位は何でしょうか。加速度の計算式を思い出してみましょう。

    v=a\cdot t

    ですね。これを変形して加速度=の式にすると

    a=\frac{v}{t}

    となります。さて、もうすぐです。vとtを単位で書くと

    ?=\frac{m/s}{s}

    となりました。メートル毎秒を秒で割るようです。この式は少し綺麗に出来ます。綺麗にすると

    \frac{m/s}{s} = m/s^2

    となります。これはちょっとダサイけど「メートル毎秒毎秒」といいます。sが2乗になってますけど、基本は
    m/sをsで割っただけの話です。

    加速度の単位

    m/s^2

    問題

    1. はじめ、速度が0だった物体が加速して1秒後に3(m/s)、そして2秒後に6(m/s)になりました。この物体の加速度は何でしょう
    2. 加速度が 5 (m/s^2)のロケットが打ち上げられました。打ち上げ10秒後の速度は?
    3. 時速140 (km/h) でタイムスリップする車があります。今は止まってますが、ちょうど10秒後にタイムスリップしなきゃいけません。以下の中から必要な加速度を選んで下さい。

     

     

    正解

    1. 3 (m/s^2)
    2. 50(m/s)
    3. 3.9(m/s^2)

    問3の考え方

    時速140km/hというのは140000m/hです。そして1時間は3600秒なので140km/hというのは140000/3600 (m/s)ですね。計算すると38.88…(m/s)です。10秒後にこの速度になればいいのですから1秒ごとだと 38.888/10で 約3.9(m/s^2)ですね。この加速度があれば10秒後にタイムスリップ出来そうですね。

  • モンティ・ホール問題を気持ちよく考える

    モンティーホール問題はとはちょっと理解し難い確率の問題です。内容は司会者がお客さんにドアを選ばせるというものです。

    Untitled_Artwork-8

    (司会者)「さて、3つのドアがあります。1個だけ正解のドアです。選んでください」
    と司会者がお客さんに選ばせます。そして、扉を開ける前に
    (司会者)「ちなみに残りの2このうちこっちは、、、空です!」
    と1つ開けてしまいます。そして
    (司会者)「今ならドアを変えられますが。変えますか?」

    さて、ここで変えるべきかどうかという話です。
    正解は「変えるべき」というものです。

    元々の確率は1/3でした。では、空のものが分かった後は?1/2??
    違います。元々のものは1/3ですが、もう1個の選んでもいなくて開けてもいないものは2/3の確率なのです。

    これが気持ち悪いですね。
    「開けたあとに元々選んでいた1/3の確率も1/2に上がるはずだ!」
    と思いますよね。

    でも違うんです。
    計算してみましょう。最初のドアが正解の確率は1/3です。つまり2個はハズレなのですから
    「1/3の確率で残り2個は両方共ハズレ(既に正解してる)」です。ということは
    「2/3の確率で残り2個のどっちかが正解」です。
    さて、司会者がドアを開けました。すると
    「1/3の確率で残り1個はハズレ(既に正解してる)」で、
    「2/3の確率で残り1個が正解」なのです。

    多分ここまででもしっくりこないはず。そういう人は司会者のドアの開け方を気にしてみてください。
    「客が選んだドアを司会者が開けることはない」
    のです。
    全てのドアは平等に扱われていないんです。この段階で確率がズレます。
    もし司会者が選んだドアすら開けて見せて「これは空です」ってやったのなら確率は1/3と1/3のままですね。

  • 誤差逆伝搬法その1

    前回ニュートン法と勾配法の話をしました。
    今回はそれと教師データを元にどうやって、関数のパラーメーターを調整していくのか、やりながら見てみましょう。ニューラルネットは出てこないですが、ニューラルネットも同じことやります。

    y=ax

    まず、ある教師データがあります。ある入力をした時にこういう出力であるべきっていうデータですね。そして、今回はそのある入力値xに対して出力値yが比例っぽい(y=axの形)ということが分かっている前提でやってみましょう。

    Untitled_Artwork-9

    教師データは上の図の赤のてんてんです。x軸が入力値でその時の出力が赤い点の値になるのが理想です。赤い点はバラけているので本当はy=axにピッタリ合うわけじゃないのですが、これにすごく近い無難なy=axとなるaを探してみます(グラフが単純なので、見ればa=1ぐらいかなって思えますね)。ただ、どの数値がどうなったら”いいa”だと言えるでしょうか。前回の話を思い出すと、理想的な答えである教師データに近ければいいわけです。具体的には教師データのxを入れた時の結果が教師データのyとどのぐらい近いのか。そのズレは「誤差」って呼ぶんでしたね。ここで、教師データがN個あって、あるi番目のデータをyi xiとするならば。その誤差は

    y_{k}-ax_{k}

    となると思います。
    これがすべての教師データで小さければ小さいほどいいわけですから、これを全部足した値が小さければいいということになります。これを式で表すと

    E_{(a)} = {\displaystyle \sum_{k=1}^{N}} |y_{k}-ax_{k}|

    こんな感じ。絶対値にするのはプラス側にズレてもマイナス側にずれてもズレとして足すためです。もし、教師データyとあるaで計算したaxが全部の教師データで同じなら誤差は0ってことになりますから目指すaはこれを0にするか、0にならなくても他に比べて小さくなるようなaを探すことです。ある関数の小さな値を探す、、、これどっかでやりましたね

    ここでやっと勾配法の登場

    ある関数の逆関数はわからないけど、とにかく小さくなる値がどの辺かを調べたい。
    ここで優秀な皆さんは前回やった勾配法を思い出すわけです。E(a)が小さくなるaを探す。そのために

    1. 適当なaを決める
    2. 関数の微分とそのaを使って計算した微分値からどっちにaを動かしたほうが値が小さくなるかを見る
    3. その勾配に合わせてaをちょっと動かす。2へ戻る

    を繰り返すわけです。今回の小さくしたい関数は誤差値であるE(a)ですが、これはラッキーな事に微分可能です。微分してみましょう。これが元々の関数ですね。

    E_{(a)} = {\displaystyle \sum_{k=1}^{N}} |y_{k}-ax_{k}|

    絶対値が邪魔なので二乗しちゃいます。

    E_{(a)} = {\displaystyle \sum_{k=1}^{N}} (y_{k}-ax_{k})^{2}

    2乗しても大小関係は変わらないですよね。でも、2乗するとマイナスがなくなるので絶対値でなくてもよくなるわけです。これをaについて微分しますと

    \frac{\partial E_{(a)}}{\partial a} = -2{\displaystyle \sum_{k=1}^{N}} (y_{k}-ax_{k})x_{k}

    こうなります。ちなみに他にも変数があるくせにaだけで微分するのを偏微分と言います。知らない人はググってください。簡単に説明すると、他にも変数はあるけどとりあえずaだけで微分してaを増やすと結果が増えるのか減るのかが分かるのがaについての偏微分です。さて、これでE(a)の微分が求められました。これでやることはわかりますね。
    例えば適当にa=100とか入れたら E(100)=なんとか ってかんじで結果が分かりますね。で、微分値である E'(100)が例えば-50だとします。これはつまり「E(a)において、a=100の場所ってのはaを1増やすと結果が50減っちゃう傾きがある」ってことです。これはもうaを増やすしか無いです。勾配法でいくと、せっかくだから傾きの値を使って傾きが大きければaをより沢山、すくなければより少なく動かそう。で、ちょっとずつ動かしていけばオーバーしないだろう。っていうやり方で行きます。だから、適当に決めたaは

    a_{next} = a_{old} - \varepsilon \frac{\partial E_{(a_{old})}}{\partial a}

    を元に今のaの値(a old)から次のaの値(a next)を計算します。ε(イプシロン)は何か適当な小さな値って意味です。数学や物理の世界でよく出てきます。これはつまり「aの値を今の傾きを元に小さくなる方へ動かす!ただし、怖いから小さいεをかけてちょっとずつ動かす」ってことです(前回やったビビリ係数です)。

    ちょっと長い話になったのでまとめますと

     

    1. 教師データとの誤差を表す関数を作る
    2. 1.の関数が小さい場所を探したいので1.の関数を微分する
    3. 適当なパラメーターを決めてその時の微分値を計算する
    4. 3の結果(勾配)を元にパラメーターをちょっとだけ動かして3をもう一度やる。

    これで繰り返しながら誤差が小さくなる場所を探すわけです。
    ちなみにaがある値の時の微分値を計算するのに全部の教師データを使いますね(シグマのところで)こうやってパラメーターをちょっと動かすときに全部のデータを使うの一括更新学習法って言います。逆にちょっとずつ使うようなのを随時更新学習法って言います。

    ここまでは単純にある教師データに近い関数のパラメーターをどうやって探すか実際に式を使ってやってみました。
    次回はこれをニューラルネットワークでやってニューラルネットワークを学習させてみましょう。