多層パーセプトロンをPythonで実装してみた-1-
A Neural Network Playgroundのデモが素晴らしかったので
Pythonを使ってそれっぽく動く物を作ってみた。
開発環境
- Visual studio 2015 community
- PTVS
- Python3.5.2
- numpy 1.11.1
- scipy 0.18.0
- scikit-learn 0.18
- matplotlib 1.5.3
多層パーセプトロン
ニューラルネットワークはまったくの初心者だったので色々調べると
多層パーセプトロンを使えばできそうなのでそれについて調べた。
順方向の計算は簡単
逆誤差伝播法はかなり難しい・・・
出力層はなんとか自分で計算できたが隠れ層は無理。
↓も正解か自信がない(手書き文字認識は問題なく出来たから大丈夫だとは思う)
+出力層
+隠れ層
学習は1データ(x,yの値と期待値y)毎に更新するオンライン学習
重み更新は学習率固定
ニューロンレイヤークラス
まず基本となるレイヤークラスを作成して
それを継承して出力層クラスと隠れ層クラスを作成した。
playgroundのデモは値の範囲が-1~+1までなので活性化関数はtanh(x)を使った。
※11/1複数の出力層に対応する様修正
# -*- coding: utf-8 -*- import numpy as np class neuronLayer(object): """レイヤー基本クラス""" def __init__(self,input_num,neuron_num,output_num,e,bias=None): self._input_num = input_num self._neuron_num = neuron_num self._ouput_num = output_num self._e = e if bias is None: self._isBias = False else: self._isBias = True #biasを追加する拡張 self._input_num += 1 self._bias = np.ones([self._neuron_num,1]).reshape(self._neuron_num,1) #初期化 self._x = np.zeros(self._input_num*self._neuron_num).reshape(self._neuron_num,self._input_num) self._s = np.zeros(self._neuron_num) self._y = np.zeros(self._ouput_num*self._neuron_num).reshape(self._neuron_num,self._ouput_num) self._delWdelE = np.zeros(self._input_num*self._neuron_num).reshape(self._neuron_num,self._input_num) self._delSdelE = np.zeros(self._ouput_num*self._neuron_num).reshape(self._neuron_num,self._ouput_num) #重みランダム初期化 #self._weight = np.random.rand(self._neuron_num,self._input_num)*2-1.0 #http://deeplearning.net/tutorial/mlp.html self._weight = np.asarray( np.random.uniform( low=-np.sqrt(6. / (self._input_num + self._ouput_num)), high=np.sqrt(6. / (self._input_num + self._ouput_num)), size=(self._neuron_num, self._input_num) ) ) def activationFunction(self,x): return self.tanh(x) def dActivationFunction(self,x): return self.dtanh(x) def sigmoid(self,x): return(1.0/(1.0+np.exp(-x))) def dsigmoid(self,x): return self.sigmoid(x)*(1-self.sigmoid(x)) def tanh(self,x): return np.tanh(x) def dtanh(self,x): return 1.0-self.tanh(x)*self.tanh(x) def ReLu(self,x): return x * (x > 0) def dReLu(self,x): return 1.0 * (x > 0) def foward(self,x): #biasが有効のときはbiasを追加 if(self._isBias == True): self._x = np.hstack((x,self._bias)) else: self._x = x # s = Σ(x*w) if(self._neuron_num == 1): self._s = (self._x * self._weight).sum() else: self._s = (self._x * self._weight).sum(axis=1)+np.zeros(self._ouput_num).reshape(self._ouput_num,1) self._y = self.activationFunction(self._s) def updateWeight(self): #print(self._weight) self._weight -= self._e * self._delWdelE def getY(self): return self._y def getWeight(self): if(self._isBias == True): #前段はbiasの重みが不要なので削除して返す return np.delete(self._weight,self._input_num-1,1) else: return self._weight def getDelSdelE(self): return self._delSdelE class inputLayer(object): def __init__(self,input_num,neuron_num,output_num): self._input_num = input_num self._neuron_num = neuron_num self._ouput_num = output_num self._x = np.zeros(self._ouput_num).reshape(self._ouput_num,1) self._y = np.zeros(self._input_num*self._ouput_num).reshape(self._input_num,self._ouput_num) def foward(self,x): self._y = x + self._x def getY(self): return self._y class outputLayer(neuronLayer): def backprop(self,y): if(self._neuron_num==1): self._delSdelE = (self._y -y) * self.dActivationFunction(self._s) else: self._delSdelE = (self._y -y) * self.dActivationFunction(self._s) self._delSdelE = self._delSdelE.T self._delWdelE = self._delSdelE * self._x class hiddenLayer(neuronLayer): def backdrop(self,delSdelE,wn): if(self._ouput_num==1): self._delSdelE = (delSdelE * wn).sum() * self.dActivationFunction(self._s) self._delSdelE = self._delSdelE.reshape(self._neuron_num,1) else: self._delSdelE = (delSdelE * wn).sum(axis=0).reshape(1,self._neuron_num) self._delSdelE *= self.dActivationFunction(self._s[0,:]) self._delSdelE = self._delSdelE.T self._delWdelE = self._delSdelE * self._x
実際に分類したりMNISTで手書き文字認識したりは次の記事に書く。
LPC1114FN28でマルチコプター作ってみた-フライトコントローラー-
マルチコプターのフライトコントローラを作ったときのメモ
プロポ入力
S-BUSが使いたかったのでFutaba 10Jを採用
S-BUSはFutabaの規格で仕様は公開されてないが
解析している人がいたのでありがたく使わせてもらった
Futaba S-BUS controlled by mbed | mbed
反転したボーレート100kのUARTなので
74AC04で反転してLPC1114FN28のシリアルポートで受信した
S-BUS2だとテレメトリーの情報をプロポに戻せるので対応したかったが
残念ながら実装するスペースがなかった
情報は↓に載ってた
sbustelemetrysensors.blogspot.jp
6軸センサー
Amazonでも売ってるMPU-6050を採用
I/FもI2Cなのでマイコンとの接続も楽チン
センサー周りはプログラムも含め別途記事にする予定。
回路図
基板を作成するために回路図を作成した。
まず、ブレッドボードで様子見して基板を作成したが、
ブレッドボードで飛ばした時に比べ基板を使うとと明らかに挙動が悪くなった。
原因はブレッドボードに比べ、基板はしっかりフレームにねじ止めしたので
モーターの振動が6軸センサーに悪影響を及ぼしたと思う。
センサー値にLPFをかけたりしたが改善しなかった。
なので、最終的にはブレッドボードのままにした。
なお、先日のマルチコプター本体は超音波センサーとFXMA108は
スペースが足りず実装しなかった。
プログラムについてはまた後日記事にする予定。
LPC1114でマルチコプター作ってみた-フレーム-
マルチコプターのフレームを作ったときのメモ
フレーム材料
材料は悩んだが入手性・強度・軽量・加工性を考えてアルミの角材+アルミ板を採用
カーボンは理想的だけと加工は大変だし何より高いので不採用
木材は加工が容易だが強度の見積もり方法が全く分からなかったのでこれも不採用
木材で工作している人は強度をどうやって計算しているのだろう?
強度計算
強度計算は構造計算に使う計算式を使った。
公式集−構造計算 片持ち梁 (曲げモーメント、せん断、反力、たわみ・・)
中心部を支点、プロペラ位置を荷重点と考えて計算
↓がホームセンターで売ってるアルミ材を元に計算した結果
上図から15×15×1.5を採用
なお、重さだかどうするか悩んだ結果以下の見積もりにした
・本体重量は1.5kg(モーター1個当たり375g)
・4Gまで耐える
・安全率は5
なので合計の7.5kgとした。
この見積もりで問題ないかは全く不明・・・
結果論だがマルチコプターが墜落しても壁にぶち当たってもフレームには全く問題が起きなかった。
設計図
検討結果から書いた設計図
LPC1114+CMSIS Libraryでprintfを使ってみた
LPC11xx_cmsis2_Libに実装されているUARTSend関数を使ってシリアル出力していたが
文字列以外の表示が面倒なのでprintfを使えるようにした。
今の所2種類の方法で動いたので両方メモしておく。
試した環境
- LPCXpresso_7.7.2_379
- LPCXpresso1114_cmsis2.zipをインポート済み
C:\nxp\LPCXpresso_7.7.2_379\lpcxpresso\Examples\NXP\LPC1000\LPC11xxにある - Redlib(none)
- LPC-Link2(Semihostingを使う際は必要)
Semihostingを使う
LPC-Link2があるならSemihostingを使うとLPCXpresso上でprintfの結果が見える。
使い方も簡単でNew project
でC Project (Semihosted)
を選ぶだけ。
出てきたサンプルコードを実行するとLPCXpresso上のConsoleにHello Worldが表示される。
syscallを実装してUART経由でprintfを使う
New project
->C Project
で作成したプロジェクト上でprintfを呼び出すと
Redlib(none)ではSystemcallが実装されていないのでリンク時エラーが発生する。
c:/nxp/lpcxpresso_7.7.2_379/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m\libcr_c.a(fpprintf.o): In function `printf': fpprintf.c:(.text.printf+0x38): undefined reference to `__sys_write' c:/nxp/lpcxpresso_7.7.2_379/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m\libcr_c.a(_deferredlazyseek.o): In function `__flsbuf': _deferredlazyseek.c:(.text.__flsbuf+0x88): undefined reference to `__sys_istty' c:/nxp/lpcxpresso_7.7.2_379/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m\libcr_c.a(_writebuf.o): In function `_Cwritebuf': _writebuf.c:(.text._Cwritebuf+0x16): undefined reference to `__sys_flen' _writebuf.c:(.text._Cwritebuf+0x26): undefined reference to `__sys_seek' _writebuf.c:(.text._Cwritebuf+0x3a): undefined reference to `__sys_write' c:/nxp/lpcxpresso_7.7.2_379/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m\libcr_c.a(alloc.o): In function `_Csys_alloc': alloc.c:(.text._Csys_alloc+0xe): undefined reference to `__sys_write' alloc.c:(.text._Csys_alloc+0x12): undefined reference to `__sys_appexit' c:/nxp/lpcxpresso_7.7.2_379/lpcxpresso/tools/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv6-m\libcr_c.a(fseek.o): In function `fseek': fseek.c:(.text.fseek+0x18): undefined reference to `__sys_istty' fseek.c:(.text.fseek+0x3c): undefined reference to `__sys_flen' collect2.exe: error: ld returned 1 exit status
Systemcallの定義はlibconfig-arm.hに記述されているので適当に実装してみた。
以下サンプルコード
#ifdef __USE_CMSIS #include "LPC11xx.h" #endif #include <stdlib.h> #include <stdio.h> #include <string.h> #include "uart.h" #include <cr_section_macros.h> // TODO: insert other include files here // TODO: insert other definitions and declarations here void uart_putc(const char); ////////////////////////// Systemcall ////////////////////////// int __sys_write(int iFileHandle, char *pcBuffer, int iLength) { int n; for (n = 0; n < iLength; n++) { if (pcBuffer[n] == '\n'){ uart_putc('\r'); } uart_putc(pcBuffer[n]); } return iLength; } int __sys_istty(int handle) { return 1; } int __sys_flen(int handle) { return 0; } int __sys_seek(int handle, int pos) { return 0; } void __sys_appexit (void) { } ////////////////////////// UART //////////////////////// // send a character via TXD void uart_putc(const char c) { // Wait for TX buffer empty while (!(LPC_UART->LSR & LSR_THRE)); // Put a character LPC_UART->THR = c; } ////////////////////////// Main ////////////////////////// int main(void) { UARTInit(115200); // Force the counter to be placed into memory volatile static int i = 0 ; // Enter an infinite loop, just incrementing a counter while(1) { i++ ; if (i % 1000000 == 0){ printf("i = %d(dec) ",i); printf("%08x(hex)\n",i); } } return 0 ; }
uart_putc関数はエレキジャックのHPにあったコード*1を
そのまま使わせて頂きました。
実行するとUARTからprintfの結果が送られてくる。
i = 1000000(dec) 000f4240(hex) i = 2000000(dec) 001e8480(hex) i = 3000000(dec) 002dc6c0(hex) i = 4000000(dec) 003d0900(hex)
scanf
printfが動作したので、ついでにscanfも実装しようとしたがうまく動かなかった。
__sys_read
と__sys_read_c
を追加で実装したが
デバック実行でブレイクポイントを設定しても止まらなかった。
Redlib内の動きが分かれば良いのだが
ソースコードは公開されていない為scanfの実装はあきらめる。
参考にしたサイト
CypressでEZ-USB FX3 のキットを買ってみた
ZedboardとPC間で高速大容量通信の実験をしようと思いUSB3.0のキットを買ったので購入方法などのメモ
買ったのはCypressの「EZ-USB FX3 SuperSpeed Explorer Kit」$49と安価なのでこれにした。
CYUSB3KIT - 003 EZ - USB® FX3™ SuperSpeed Explorer Kit - Cypress
ただし、このキットはピンヘッダで外部と接続する為そのままではZedboardには接続できない。
CypressでFMCやHSMCに変換するボードが別途販売しているのでそれも買う必要がある。
CYUSB3ACC - 006 HSMC Interconnect Board for the EZ - USB® FX3™ SuperSpeed Explorer Kit - Cypress
CYUSB3ACC - 005 FMC Interconnect Board for the EZ - USB® FX3™ SuperSpeed Explorer Kit - Cypress
今回はAlteraSocでも試せる様に両方入手した。
Explorer KitはDigikeyで販売しているが、FPGAに接続するボードは見つからなかったのですべてCyptessから直接購入した。
Cypressで購入するにはそのままカートに追加していけば良い。
購入にはユーザーアカウントが必要だが、ZedBoardでUARTを使う為Cypressでドライバをダウンロード
した際に作成していたのでそのアカウントを使用した。
送料は$29か$39を選べ$39だと早く到着する様です。今回は$39にしてみた。
支払いはクレジットカードが使えたので簡単に決済できた。
注文を12/1のお昼頃にして、12/5帰宅するとFedExの不在票が入っていて明日、日通にて再配達すると書いてあり12/6に受け取った。
Explorer Kitの箱
ZedboardにFMEボードを介して接続した写真
Simple framebufferを有効にする
Linux上からディスプレイに出力する方法を調べているとSimple framebufferという物がある様なのでqemu上で試したメモ。
qemuは前回のqemu2svの環境を流用。
qemu側の変更点
sysbus_create_simple("hw_if", 0xE1000000, NULL);
kernel側の変更点
make menuconfig
を実行
Simple framebuffer supportを有効にする。
Device Drivers→Graphics support→Support for frame buffer deviceと選択する。
device treeに以下を追加
ps7_fb: framebuffer@E1000000 { compatible = "simple-framebuffer"; reg = <0xE1000000 (800 * 600 * 2)>; width = <800>; height = <600>; stride = <(800 * 2)>; format = "r5g6b5"; };
ひな形は↓にあった。
Documentation/devicetree/bindings/video/simple-framebuffer.txt
qemu起動
起動後にdmesg
を実行するとログ内でsimple framebufferが認識しているのがわかる。
simple-framebuffer e1000000.framebuffer: framebuffer at 0xe1000000, 0xea600 bytes, mapped to 0xf010000 0 simple-framebuffer e1000000.framebuffer: format=r5g6b5, mode=800x600x16, linelength=1600
また、/dev/fb0
が追加されている。
もうちょっと確認する
hw_if.cを修正する。
hw/char/hw_if.c
static void hw_if_write(void *opaque, hwaddr offset,uint64_t value, unsigned size){ if (value != 0) { printf("write::%lx %lx %x\n",offset >> 2,value,size); } }
value != 0
がないと黒のドットがすべてprintされるので0以外を出力している。
この状態でecho -ne "a" > /dev/fb0
とやってみた。
zynq> echo -ne "a" > /dev/fb0 write::0 61 1
0x61はASCIIコードで"a"なのでライトできてる様です。
実機に実装する際はデュアルポートメモリを置いて片方は0xE1000000にバスからライトして片方はVGAなりHDMIなりに同期信号付けて渡せば動作しそう。
リフレッシュレートの設定が見つからないけどLinuxは同期をどうしてるんだろう?書けるだけ書いているのかな?