• 2. 人工で再現するには

    前回、人のニューラルネットワークは神経細胞が沢山つながっていて、1つの細胞が出力するかどうかはその細胞への入力があるレベルを超えたかどうかで決めると話しました。そんな細胞がたくさんあることで人間ができているのならば何とか再現して人間を作れそうですよね。別に人間の作り方を書きたいわけではないですが、そう考えるとワクワクします。
    では早速、ニューラルネットワークを人工的に作るやり方を見ていきましょう。

    モデル

    人工ニューラルネットワークを考える上で、神経細胞をこんな風にモデル化してみます。
    newralmodel2

    左側のx1,x2,x3が入力で、それが中央のθ(シータ)が書いてある丸に集まります。これが1つの神経細胞です。そして出力が右側から出ています。
    θというのは前回出てきた「入力がこのレベルを超えたら出力する」の”このレベル”のことです。入力の合計がシータを超えたら出力するわけです。この値のことを閾値(しきいち)といいます。
    入力x1,x2と細胞の間にw1,w2というのが書いてありますが。これはそれぞれの重みです。
    すごく単純に考えれば場合は x1 + x2 + x3 がθを超えたかどうかで出力すればよいのですが、「この細胞からの入力は大事」「この細胞からの入力はどうでもよい」といったことをしたいので重みを用意しています。このwはこのように使います。

    input = w_1x_1+w_2x_2+w_3x_3

    つまり、重みが0だとどんなに大きな入力xが来ても無視されるし、重みが大きいと、小さな入力でも重要視されます。入力がNこあったとすると、重みを考えた入力の合計(=細胞内電位)は以下のようになります。

    input = {\displaystyle \sum_{i=1}^{N}} w_{i}x_{i}

     

    出力は決定的か確率的か

    入力に重み付けをするところまでは分かりました。あとは、出力するかどうかはこんなふうにすれば良さそうです
    出力する = 入力(細胞内電位)が閾値を超えた
    出力しない = 入力(細胞内電位) が閾値を超えてない
    プログラム(C言語)的に書くとこのようになります。

    if(入力の合計 > 閾値){
     // 出力する
    }else{
     //出力しない
    }
    

    このように「入力が決まったら出力が1つに決まる」ことを決定的であるという言い方をします。
    つまり、閾値が100だとして、入力の合計が90なら絶対出力しないし、101なら絶対出力します。これが決定的だということです。

    これを神経細胞の代わりに使ってたくさん用意してつないでいってもいいのですが、ここで問題が。細胞を観察すると入力に何も入れていないのに、たまに自発的に(=自分から)出力することがあるそうです。「あ〜暇だな〜ちょっと出してみるか」とでも言わんばかりに出力してきます。
    入力してないわけですから、閾値を超えてないですよね。今までの話と合いません。つじつまを合わせるために、どうやら神経細胞は決定的ではなく確率的に動くと考えたほうが良さそうです。

    確率的というのは「入力により出力の確率だけが変わる」ということです。つまり、入力を大きくすると出力する確率が上がるのです。だから、入力が大きいからといって絶対出力するってことではないということです。
    宝くじに似てます。たくさん買えばそれだけ1等の確率が上がります。逆に、買ったのが1枚だとしても当たるかもしれない。それでも、数が多ければ確率は上がっていきます。
    細胞もそうならば、入力がなくても出力したっておかしくありません。
    細胞の入力で出力の確率が決まるんだとして進めましょう。

    では、問題はどんな風に確率が決まるかです。似たような関数はないものでしょうか。動きは分かっています。入力が弱かったら確率が限りなく0になって、強かったら限りなく100%に近づく。で、閾値のあたりで出力の確率がグンと上がる、そんな関数はなんでしょう。

    シグモイド関数

    ちょうどいい関数があります。シグモイド関数です。シグモイド関数は

    sigmoid(x) = \frac{1}{1+{e}^{-\alpha x}}

    このような形をしています。αが1だとして、グラフはこのようになります。
    400px-SigmoidFunction
    出典:wikibooks

    xがマイナスに向かうと限りなく0となり、プラスに向かうと限りなく1(=100%)となります。また、0が閾値になっていて、0を境に変化します。
    このグラフはαが1のときのものですが、αを変えるとこのように変わります。
    sigmoic_half1
    出典: beigebag.com

    αが大きくなるほど0付近で大きく変化するようになります。αはゲインと呼ばれています。
    ちなみに最初に出てきた決定的な動作というのを同じようにグラフにすると
    400px-HardLimitFunction

    このようなグラフになります。確率は決定的なので0%か、100%しかなく、閾値を境に切り替わります。確率的であるシグモイド関数も、αを大きくしていくといつかはこのような動きををするようになることから、決定的な動作というのは確率的な動作の一部であると考えられます。

    さて、欲しかった関数としてはシグモイド関数がちょうどよさそうなので、これを使って考えていきましょう。
    入力は上の方に書きましたので、これとシグモイド関数を使うと、ある入力で出力するかどうかの確率は

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

    となります。シグモイド関数のxに入力の合計-θが入っただけです。θは閾値ですね。

    注意しなければいけないのは、グラフを見る限りアナログ的ですが、細胞の出力は相変わらず0か1しか無いということです。シグモイド関数はあくまで入力がいくつの時に、出力が1になる確率がどのぐらいあるかを決めているだけで、その入力があった時に本当に出力が1になるかどうかは不明なのです。

    何故シグモイド関数なのか

    シグモイド関数以外にも同じような形になる関数はあるのに、何故シグモイド関数を使うのでしょうか。
    実は後のほうでニューラルネットワークの学習方法というのをやるのですが、その時にこの確率を決める関数を微分する必要が出てきます。
    シグモイド関数は微分しても形がほぼ変わらないのです。微分する前の値を微分した後にも使うことができるので計算が高速化出来るのです。

    今日のまとめ

    • 実際の細胞は入力がなくても出力することがある。つまり入力は出力するかどうかの確率を変えているだけと考えられる
    • その確率を決める関数としてはシグモイド関数がちょうど良さそう
  • STM32F3Discover GPIO input

    前回はGPIOを使った出力でした。
    今回は入力をしてみましょう。

    と、いっても前回でGPIOはほとんどやってしまったのですごく簡単です。
    前回ちゃんと見た人ならまず、ピン設定を入力にするにはどうしたらいいかわかるはずです。
    MODERを0にします。これで入力になり、対応するピンに掛かる電圧の大きさからIDRの対応するビットが0か1になります。
    ちなみに今使ってる基板にはUSER用のボタン(多分青色)があって、これはA0につながっています。
    回路図を見ればわかりますが、ボタンにつながったA0はボタンを押すと電源電圧、離すとGNDになります。
    今回はA0のONOFFをLEDのONOFFにしちゃいます。
    やることとしては、GPIOAのクロックをONにする。モードを入力にする、プルアップ・プルダウンをなしにするでOKですね。
    前回は出力だったのでOTYPERやODRを設定しましたが、入力ならなしです。

    [c]

    #include "stm32f30x.h"

    void SystemInit(void)
    {

    }

    int main(void)
    {
    // connect AHB
    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    RCC->AHBENR |= RCC_AHBENR_GPIOEEN;
    GPIOA->MODER = 0x00000000;
    GPIOA->PUPDR = 0x00000000;
    GPIOE->MODER = 0x00010000;
    GPIOE->OTYPER = 0x0000;
    GPIOE->PUPDR = 0x00000000;
    GPIOE->ODR = 0x0100;
    while(1){
    GPIOE->ODR = GPIOA->IDR << 8;
    }
    }

    [/c]

    これが今回のプログラム。

    while文の無限ループの中で GPIOAのIDRを8bit左にシフトしてGPIOEのODRに入れています。
    ボタンはA0で、LEDはE8なのでIDRを8bit左シフトすることで ボタンのONOFFがちょうどLEDの出力になるようにしています。
    (もちろん他のビットも一緒にシフトしているのでもし、他のところにもボタンがあったらODRに反映されます。今回は無視しています)

    今回はあっさりと終了

  • STM32F3Discover GPIO

    マイコンをやったら最初にやること。LEDの点灯です。
    まずはこれからやってみましょう。

    [ GPIO ]
    マイコンでは、入力したり出力したりするもののことをI/Oといいます。LEDをつけるときもIOを使います。
    特にARMではI/OをGPIOと呼んでいます。General Purpose IO(汎用的なIO)という意味らしいです。
    STMDiscoveryのドキュメント023594.pdfにはこんな表があります。
    これはボード上でどのピンが何につながっているかです。これをみれば基板にある8このLEDがマイコンのどこにつながってるかわかります。

    どやらLEDはPE8~PE15につながってますね(PE15はスクショの関係で切れちゃいましたがPE14の下にありあす。ごめんね!)
    PEって何かというとGPIOEの略です。
    マイコンにはたくさんのピンがあって、それのONやOFFをプログラムから制御できます。それができてこそマイコンだよね。
    しかし、すべてのピンを ピン87番とかピン111番というふうに名前をつけて制御することはできません。どうしてもグループ化する必要があるのです。
    このマイコンではすべてのピンをグループA~グループFまでの6個のグループに分けてあります。
    GPIOA
    GPIOB
    GPIOC
    GPIOD
    GPIOE
    GPIOF
    そして、それぞれが15個のピンを制御します。
    LEDにつながっているのは、グループとそのグループの中の番号で言うと、グループFの8番から15番ということですね。
    だから、このピンをONにしてLEDをつけたりOFFにしてLEDを消すには、
    プログラム的にはグループEの8番をONにしたりOFFにしたり と言ったプログラムをかくことになります。
    では、実際にやってみましょう。

    [ レジスタ操作 ]
    STM32F30シリーズで、GPIOの出力状態を制御しているのはODRというレジスタです。
    出力状態をレジスタで制御するというのはどういうことでしょうか。
    referencepdfの22558の8.2.6にこんな表があります。

    これがODRというレジスタです。32ビット分ありますが、15bitから0bit目までがそれぞれそのGPIOの0~15の出力状態の設定になってます。
    どういうことかというと、例えば、GPIOEの8にはLEDがつながってますね。このLEDを点けるにはGPIOEのODRレジスタの8ビット目を1にすればいいんです。
    すると、「GPIOEの8は出力状態が1ね」と設定したことになります。すると、GPIOEの8番ピンには電圧がかかり、LEDがつきます。
    逆に0にしたらLEDはつきません。GNDに接続されたのとおなじになります。

    今回のミッションはGPIOEの8番につながったLEDをつけることとします。すると、GPIOEのODRには
    0x0100
    を書き込めば、ちょうど8bit目だけ1になりここのLEDだけつきますね。
    これをプログラムするとこういう感じになります。

    #include “stm32f30x.h”

    void SystemInit(void)
    {

    }

    int main(void)
    {
    GPIOE->ODR = 0x0100;
    while(1){
    }
    }

    余計なinclude文が出てきました。
    これはMDK-ARMにセットで入ってきたもので、STM32F30x系のマイコンで、どのレジスタがどこにあるとかってのが定義されています。
    GPIOEのODRにアクセスしたいときは
    GPIOE->ODR ってすればアクセスできます。ここに値を書きたい場合は
    GPIOE->ODR = 0;
    みたいにすればOKです。今回はここに0x0100を書きたいので、main関数始まってすぐに
    GPIOE->ODR = 0x0100;
    を実行します。

    ただ、このプログラムだけだとLEDはつきません。
    GPIOEはピンへの出力ができますが、入力もできます。つまり、逆にピンに電圧がかかってるかどうかを01で知ることもできるわけです。
    つということは、ピンごとに入力なのか、出力なのかを設定するところがどっかにあるわけです。そして、それはマイコンの電源投入後にはすべて入力になっているのです!
    これじゃあODRに1をセットしても出力されません。
    それを設定するレジスタを紹介します。というか、この際なので、GPIOに関係したレジスタを全部紹介しましょう。

    – MODER  ピンごとに デジタル入力 アナログ入力 デジタル出力 AlternativeFunctionなのかを決める
    – OTYPER  出力モードがプッシュプルか、オープンドレインかを選ぶ
    – OSPEEDR  ピンごとの速度設定
    – IDR  デジタル入力状態
    – ODR  デジタル出力状態
    – BSRR  ODRのbitwise write access
    – LCKR  このGPIOの設定ロック
    – AFRH  8~15pinのalternativefunction
    – AFRL  0~7pinのalternativefunction

    ピンには4つのモードがあります。今回使いたいにはデジタル出力ですが、他にデジタル入力とアナログ入力 があります。
    アナログ入力はA/Dで使うやつです。そして、AlternativeFunctionというのは、ピンごとにGPIO以外の機能を接続することがあるのですが、それがalternativeFunctionです。GPIOじゃなくてこっちでつかうときはMODERをこれにして、AFRHやAFRLの4bitを使って、alternativeFunctionを設定します。
    今回は関係ないので省略。
    今回関係あるのだけにすると

    MODER
    OTYPER
    OSPEEDR
    です。そしてそれぞれこのような設定があります。

    MODER
    00 input
    01 output
    10 alternative
    11 analog input

    OTYPER
    0 push-pull
    1 opendrain

    PUPDRは
    00 none
    01 pull up
    10 pull down
    11 reserved(00とほぼ同じだが基本使わない)

    MODERとPUPDRはそれぞれ2bitで1つのピンの設定をします。OTYPERはODRと同じように1bitで1つのピンです。
    MODERは出力にしたいのでGPIOEの8を01にしなきゃいけません。
    OTYPERは出力タイプです。プッシュプルとは1なら電源の+が、0ならGNDが出力されるということです。オープンドレインは0と1でハイインピーダンスとGNDを出力するものです。今回はプッシュプルでいいので0にしましょう。
    PURDRはポートごとのプルアッププルダウンをするものです。今回はいらないので00でよさそうです。
    GPIOEの8に対していままでの設定をします。
    出力モード・出力タイプはプッシュプル・プルアップダウンなし・そして出力はON

    GPIOE->MODER = 0x00010000;
    GPIOE->OTYPER = 0x0000;
    GPIOE->PUPDR = 0x00000000;
    GPIOE->ODR = 0x0100;

    するとこのようになりますね。
    これは、GPIOEの8番品以外も設定してることになります。0を書き込んでるから0を設定してることになります。
    ただ、MODERが00ということはinputなので、まぁ悪さをすることはないと思います。
    さて、これでGPIOEの8番を出力に出来ました。

    [ クロックの設定 ]
    最後にもう1つだけONにしなきゃいけない部分があります。クロックです。
    ARMはそもそも少ない電気で動くようにと設計されたものです。このSTM32F3もなるだけ少ない電気で済むように設計されています。
    余計な部分で電気を食わないように。
    マイコンにおいて電気が消費されるのは基本的にクロックが来た時です。早く動かせばそれだけ電気を食うし遅ければそれだけ電気を食いません。そして、クロックがなければ動かないので電気を食わないわけです。
    このSTM32FはGPIOを含め、ほぼすべてのモジュールに”クロックが来ていません”。設定しないとクロックが来ない->動かない のです。
    すごいよね。
    もちろんODRとかのレジスタに値を書き込むことはできますが、この値がちゃんと反映されてピンから出力されるには最低でも1回はクロックが来ないとダメです。
    クロックのタイミングでレジスタから実際の出力になるわけですから。
    というわけで、モジュールを使いたかったら使いたいモジュールのクロックをONにしないといけないわけです。

    ちなみに、STM32F3にはAHBとかAPB1とかいろいろなクロックの配線があってそれぞれ速度を減速できるようになってます。
    例えばAHBはCPUと同じ。APB1はCPUの半分とかができます。遅くすればそれだけ電気を食わないので不要な部分は速度を落とす なんてことができます。
    それぞれのモジュールはつながってる場所が決まってます。GPIOはすべてAHBにつながっています。
    RCCのAHBにおいて、GPIOEをONにしましょう。

    RCC->AHBENR |= RCC_AHBENR_GPIOEEN;

    これでOKです。さっきのドキュメントには

    こうやってかいてあります。これがAHBENRレジスタで、AHBというクロックの回線がつながってるモジュールそれぞれのONOFFになってます。
    GPIOEは21bit目ですね。reset後は0つまりOFFになっています。21bit目を0から1にすればOKです。
    さっきのヘッダファイルには
    RCC_AHBENR_GPIOEEN
    というのが用意してあって、これは21bit目だけ1となるような数値です。
    これをAHBENRにorします。つまり今AHBENRですでに1になってるものはそのままに、追加で21bit目を1にします。
    それが

    RCC->AHBENR |= RCC_AHBENR_GPIOEEN;

    です。

    これでGPIOEの8をONにできます。な、、、、ながいみちのりでしたね。

    #include “stm32f30x.h”

    void SystemInit(void)
    {

    }

    int main(void)
    {
    // connect AHB
    RCC->AHBENR |= RCC_AHBENR_GPIOEEN;
    GPIOE->MODER = 0x00010000;
    GPIOE->OTYPER = 0x0000;
    GPIOE->PUPDR = 0x00000000;
    GPIOE->ODR = 0x0100;
    while(1){
    }
    }

    完成品はこんな感じです。これを動かしてみましょう。
    前回使ったMDK-ARMのLoadを押してプログラムを転送し、基板にあるリセットボタンを押せば動きますが、今回は試しにデバッグ機能を使います。まず、このプログラムを書いたらBuildをおしてちゃんとビルドします。
    つぎにデバッグボタンを押します。

    すると警告が一度出ますがOKおしちゃいます。無料だと32KBまでだよって警告です。
    そして、Runボタンを押して実行します。

    これで実行できます。
    実行すると、基板上のLD4と書かれた青色のLEDが光ると思います。
    光ればOK。
    まとめ。
    ・すべてのI/OピンはA~Fの6つのGPIOで管理されている。
    ・出力をONOFFするには対応するGPIOのODRレジスタを0や1にする
    ・出力するには出力設定や出力モード切替が必要
    ・GPIOなどのモジュールを使うにはクロックをONにする必要がある。

  • STM32F3Discover Install MDK-ARM

    プログラムを書いてマイコンを操作するわけですが、そのプログラムを書いたりマイコンに書き込む開発環境は無料なのだけでもいくつかあります。
    その中でもMDK-ARMを使ってみましょう。
    これはARMそのものが提供しているのでSTMマイコンじゃなくて他のARMでも使えるというメリットがあります。
    無料だとコンパイル後の容量が32Kバイト未満という制限がつきますが、とりあえずは使えるしOKですね。足りなくなったら有料版を買いましょう。

    [ インストールしてみる ]

    http://www.arm.com/ja/products/tools/software-tools/mdk-arm/index.php
    ここからDLします。このURLを開いて「DownloadNow」ってのを押しましょう。

    すると登録画面みたいなのが出てきますので、入力しちゃいます。住所も入れますがなにか届いたりはしないと思います。
    すくなくとも無料なので請求書は来ません。
    i am using device from は STで、Which arm architectureはCortex-M4にチェックを入れます。

    すべて終わったら下にあるSubmitを押すとダウンロードページに行きます。
    これでMDK-ARMをDLできます。ファイル名をクリックでダウンロードが始まります。
    写真は僕がこの記事を書いた時のバージョンなので多少変わってるかもしれません。

    ダウンロードが終わるとこれと同じ名前のexeファイルがあるとおもいます。実行しましょう。
    ちょっとスクリーンショットがないのですが、普通にNextNext押していけ行けると思います(笑)

    終わるとKeil uVision4というアプリが出てくると思います。これが言っていたMDK-ARMです。

    これでインストールは完了。次回はプログラムを書きはじめてみましょう。

  • STM32F3Discover 買って動かしてみる

    STM32F3Discoverはどこにでも売ってますが、やっぱり秋月でしょうか。
    http://akizukidenshi.com/catalog/g/gM-06268/
    ここから買えます。
    購入台数を1台にして「かごに入れる」から購入します。恋人とおそろいにするなら2台ですね。嫌われなければの話ですが。
    ちなみにケーブルは入ってないのでminiUSBとUSBの変換ケーブルが必要です。
    ない人はそれも買わなきゃいけません。
    http://akizukidenshi.com/catalog/g/gC-05222/
    これです。

    届くと箱に入ってますが、ざっくり開けちゃいましょう。
    そしてPCでも充電器でもいいので何かの電源にUSBをさして、ボードにつなぎます。
    ボードにはUSBが2個ありますが。メインは真ん中に近い方のやつです。 「USB ST-LINK」って書いてあります。
    マイコンにはサンプルプログラムが書いてあって、とりあえず電源入れると とりあえずそれが動き出します。
    LEDがぐるぐる光るはず。
    ボタンが2個ありますが、青色のUSERボタンを押すとモードが切り替わります。
    ジャイロモードとか磁気センサーモードになります。

    とりあえず動きましたね。
    次回からはプログラムです、