Elm゛もぐもく会に参加しました。

オンライン上でのイベントではありましたがElm゛もぐもく会(以下もくもく会)に参加しました。

Elmとは?

Haskellっぽい見た目のコードでWebフロントエンドを書くことができるプログラミング言語フレームワークのようなものです。

結構おもしろい言語なので触ってみることをおすすめします。

今回もくもく会で自分がもくもくとやっていたのはElmの公式ドキュメントにあるコードの写経とドキュメントの下の方にあったExcerciseでした。

サイコロ

Random · An Introduction to Elmの下部にあるExcerciseでSvgを使ってサイコロの画像を表示してみようというのがあるのでやってみたのが下のものです。

https://ellie-app.com/32hNfRvrvT2a1

こちらの苦労したのはリストからランダムに値を取り出すところでした。

最初はドキュメントの方にもあったようにRandom.int 1 6で1~6までの整数をランダムに生成する関数を使い、その数字に対応するSvg画像を生成する処理を行っていました。

diceFace : Int -> List (Svg Msg)
diceFace num =
  if num == 1 then
    [
      circle [cx "50", cy "50", r "10"] []
    ]
  else if num == 2 then
    [
      circle [cx "20", cy "20", r "10"] []
    , circle [cx "80", cy "80", r "10"] []
    ]

    ...

    ]
  else
    [
      circle [cx "20", cy "20", r "10"] []
    , circle [cx "20", cy "50", r "10"] []
    , circle [cx "20", cy "80", r "10"] []
    , circle [cx "80", cy "20", r "10"] []
    , circle [cx "80", cy "50", r "10"] []
    , circle [cx "80", cy "80", r "10"] []
    ]

関数型言語であるElmはIf文は必ずelse文を持って置かなければならず6の判定はelseというゴリ押しでした。

Random.int 1 6は1から6までの値しか生成しないのでプログラムの動作上では問題ないのですが、やはりelseでの処理は気持ち悪いです。

そこに「Unionを使うといい感じになるよ」というリファクタリング案を頂いたのでコードを修正しました。

type DiceFace = One | Two | Three | Four | Five | Six

diceFaceSvg : DiceFace -> List (Svg Msg)
diceFaceSvg df =
  case df of
    One ->
      [
        circle [cx "50", cy "50", r "10"] []
      ]
    Two ->
      [
        circle [cx "20", cy "20", r "10"] []
      , circle [cx "80", cy "80", r "10"] []
      ]

    ...

    Six ->
      [
        circle [cx "20", cy "20", r "10"] []
      , circle [cx "20", cy "50", r "10"] []
      , circle [cx "20", cy "80", r "10"] []
      , circle [cx "80", cy "20", r "10"] []
      , circle [cx "80", cy "50", r "10"] []
      , circle [cx "80", cy "80", r "10"] []
      ]

これでelseで6の処理を行うという気持ち悪い部分が解決しました。

また、この際にサイコロの出る目をランダムに生成する処理を変更してRandom.int 1 6ではなく

rollDice : Generator DiceFace
rollDice = sample [One, Two, Three, Four, Five, Six] |> Random.map (Maybe.withDefault One)

elm-community/random-extraの方にあるsample : Array a -> Generator (Maybe a)

elm-core/maybeにあるwithDefault : a -> Maybe a -> a

これらの関数を使ってランダムに取り出すことにしました。

リストから取り出すにはsampleで取り出し、withDefaultにダミー初期値を与えてJustを取り出すしかないようです。

こうしていろいろ直したあとにSvgの処理なども綺麗にして以下のようになりました。

gist.github.com

HaskellでGLFWを使うための備忘録 - ウィンドウ表示編

HaskellでGLFWを使うための備忘録のウィンドウ表示編です。

OpenGLなどのGUI関連のチュートリアルでほとんど最初に行うことはウィンドウ表示です。

ウィンドウを表示するコードは以下の通りです。

GLFWの関数

今回のコードで使ったGLFW-bの関数の適当な説明をここに記していきます。

getVersionString :: IO (Maybe String)

Maybe String型のバージョン情報を取得するための関数です。

C/C++版GLFWのglfwGetVersionStringに相当。

getVersionStringは型を見るとMaybe Stringの値を返すIOアクションであることが分かるので<-関数を使ってversionという変数に束縛します。

Just version = GLFW.getVersionString
putStrLn version

MaybeモナドNothing以外はすべてJust aの型になるのでパターンマッチを使ってMaybe StringStringの値のみを抽出します。

init :: IO Bool

GLFWを初期化するための関数。

初期化していない場合、使える関数はgetVersiongetVersionStringsetErrorCallbackinitterminateのみとなります。

C/C++版GLFWのglfwInitに相当。

initBoolを返すIOアクションなのでgetVersionStringと同様に(<-)関数で束縛します。

glfwInitialized <- GLFW.init
putStrLn $ "status:" ++ (show glfwInitialized)

defaultWindowHints :: IO ()

Window Hintsをデフォルト値にする関数。

(Window Hintsについてご存知の御方はコメントにて教えていただけると助かります。)

defaultWindowHints()なので何も出力も返しません。

createWindow :: Int -> Int -> String -> Maybe Monitor -> Maybe Window -> IO (Maybe Window)

新しくウィンドウを作る関数。

GHCIで実行する際は:set -fno-ghci-sandboxを忘れずにとのことですが試してないです。

GLFWのglfwCreateWindowに相当。

引数はそれぞれcreateWindow Width Height Title Monitor Windowとなっており。

Monitorにはフルスクリーンで表示するときにどのモニターを使うか指定するときに使います。

Windowは別のウィンドウの情報を共有する際に用いるらしいです。

今回MonitorWindowは指定しないので両方共Nothingとします。

Just win <- GLFW.createWindow width height title monitor window

--------------------

where
    width = 400
    height = 400
    title = "Hello World"
    monitor = Nothing
    window = Nothing 

これもMaybe Windowを返すIOアクションなのでパターンマッチを使ってWindowwinに束縛しています。

makeContextCurrent :: Maybe Window -> IO ()

現在のウィンドウのコンテキストを作成します。

コンテキストとはOpenGLの状態を保持しているデータです。

コンテキストを変更することで表示が変わるなどします。

GLFW.makeContextCurrent (Just win)

setKeyCallback :: Window -> Maybe KeyCallback -> IO ()

対象のウィンドウに任意のキーが押された際のコールバックを設定する関数です。

GLFW.setKeyCallback win (Just keyCallback)

--------------------

keyCallback :: GLFW.KeyCallback
keyCallback win GLFW.Key'Q _ GLFW.KeyState'Pressed _ = shutdown win
keyCallback _ _ _ _ _ = return ()

KeyCallback :: Window -> Key -> Int -> KeyState -> ModifierKeys -> IO ()

GLFW.KeyCallbackWindow -> Key -> Int -> KeyState -> ModifierKeys -> IO ()の型シノニムです。

C/C++版GLFWのsetKeyCallbackによるとGLFWkeyfunという構造体に相当します。

それぞれの引数はWindow -> Key -> Scancode -> KeyState -> ModifierKeysとなります。

Keyはキーボードからの入力。

Scancodeは任意のキーのスキャンコード。

KeyStateKeyState'PressedKeyState'ReleasedKeyState'Repeating押された離された繰り返し押されたの3状態のうちいずれか。

ModifierKeysはデータ型ModifierKeysを指定します。

ModifierKeysModifierKeys :: Bool -> Bool -> Bool -> Bool -> ModifierKeysという型シグネチャのデータ型です。 4つのBool値はそれぞれShiftControlAltSuperに対応しています。

Shiftを押しながらQを押したときにウィンドウを閉じるときは以下のようにします。

keyCallback win GLFW.Key'Q _ GLFW.KeyState'Pressed (GLFW.ModifierKeys True False False False) = shutdown win

setWindowCloseCallback :: Window -> Maybe WindowCloseCallback -> IO ()

ウィンドウを閉じる際のコールバックを設定する関数です。

WindowCloseCallbackWindow -> IO ()の型シノニムです。

GLFW.setWindowCloseCallback win (Just shutdown)

雑な説明になりましたので何か間違ってる箇所とか自分が勘違いしているところも多々あると思いますので、コメントにてご指摘してくださると助かります。

参考文献

OpenGL (GLFW-b) - IT練習ノート

Graphics.UI.GLFW

HaskellでGLFWを使うための備忘録 - 環境構築編

HaskellでGLFW使って何かゲーム作りたいなという野望をそろそろ実現しようと思っていながら、今まで手をつけていなかったのでそろそろ重い腰を上げてやっていこうと思います。

本記事は備忘録なので第三者にとって曖昧であったり不可解である部分が出てくるかもしれません。

何か間違っているところがありましたら何なりとコメントでお申し付けください。

GLFWを使うにあたっての必須パッケージ(ubuntu/debian)

  • libglfw3-dev
  • libxinerama-dev
  • libxi-dev
  • libxcursor-dev

$ sudo apt install libglfw3-dev libxinerama-dev libxi-dev libxcursor-dev

stackのインストール

Haskellのビルドツールです。

ビルトインライブラリ以外のものを使うとき、stackを使うと管理が楽なのでこちらを使っていきます。

詳しくはtanakhさんがQiitaにあげている記事を参照してください。

Haskellのビルドツール"stack"の紹介

インストールは以下のコマンドで行います。

$ wget -qO- https://get.haskellstack.org/ | sh

プロジェクトディレクトリの生成

$ stack new glfw-experiment

以下のようなディレクトリとファイルができると思います。

glfw-experiment/
├── ChangeLog.md
├── LICENSE
├── README.md
├── Setup.hs
├── app
│   └── Main.hs
├── glfw-experiment.cabal
├── package.yaml
├── src
│   └── Lib.hs
├── stack.yaml
└── test
    └── Spec.hs

3 directories, 10 files

package.yamlの編集

今回はGLFW-bという外部ライブラリを使用するのでそちらを使うために設定ファイルに変更を加えます。

executablesのdependenciesにGLFW-bを追加します。

executables:
  glfw-experiment-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - glfw-experiment
    - GLFW-b

ビルドしてみる

ビルドできるか確認してみます。

$ stack build
$ stack exec glfw-experiment-exe
someFunc

someFuncと出力されればビルドは成功です。

これにてGLFWを使う準備は整いました。

Google Colabを使ってCIFAR10を学習させてみた

CPUのみで機械学習しているとどんなに良いCPUであっても学習に時間がかかります。

特に畳み込みニューラルネットワークなどのパラメーター数が10万以上のものを扱うものだと、たった2層だけでも30分くらいかかることがあります。

GPUのような行列演算に特化したプロセッサを用いると短時間で学習させることができます。

そこで自分のマシンには一応非力ながらもグラフィックボードがあるのでGPUを使っての機械学習をさせてみようと思いました。

昨日までCUDAを入れたりcuDNN入れたりtensorflow-gpuを入れたりなど環境構築を頑張っていたのですがどうにもうまく構築できませんでした。

そこでGoogleの持っている機械学習用マシンのパワーを無料で使えるとかいう話を以前聞いたことがあったのを思い出し調べた結果Google Colabを今更ながら知ることとなりました。

Google Colabとは

Googleが提供している機械学習の研究の促進を目的としたクラウド上の開発環境です。

UIはJupyter Notebookに似ており同じ感覚で使うことが出来ます。

Google機械学習用マシンのパワーを無料でお借りすることができるという素晴らしいサービスです。

今回はこれを使ってCIFAR10を8層のCNNを使って100エポック学習させてみました。

CIFAR10とは

Canadian Institute For Advanced Researchが提供している32 x 32ピクセルのRGBデータのある物体識別用データセットです。

50k個のトレーニングセット、10k個のトレーニングセットとそれぞれの正解ラベルで構成されています。

CNNのモデル

今回使用したモデルは以下のとおりです。

8層の畳み込み層で構成されています。

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 32, 32, 32)        896       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 30, 30, 32)        9248      
_________________________________________________________________
dropout_1 (Dropout)          (None, 30, 30, 32)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 30, 30, 32)        9248      
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 28, 28, 32)        9248      
_________________________________________________________________
dropout_2 (Dropout)          (None, 28, 28, 32)        0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 14, 14, 32)        9248      
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 12, 12, 32)        9248      
_________________________________________________________________
dropout_3 (Dropout)          (None, 12, 12, 32)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 12, 12, 32)        9248      
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 10, 10, 32)        9248      
_________________________________________________________________
dropout_4 (Dropout)          (None, 10, 10, 32)        0         
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 5, 5, 32)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 800)               0         
_________________________________________________________________
dropout_5 (Dropout)          (None, 800)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               102528    
_________________________________________________________________
dropout_6 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290      
=================================================================
Total params: 169,450
Trainable params: 169,450
Non-trainable params: 0
_________________________________________________________________

学習結果

トレーニングデータ誤認識率 53.4%

テストデータ誤認識率 71.6%

MNISTの学習結果と比べるととても低めです。

畳み込み層を多くしたせいでHigh Varianceなモデルになってしまったことが原因だと考えられます。

しかしこんなにも畳み込み層が多いモデルであるのに学習時間は1エポックあたりたったの30秒でした。

Googleのマシンパワーの素晴らしさに驚き、このパワーを無料で提供してくれるあたりさすがGoogle様ですね。

今回のNotebook

gist.github.com

CNNを少しかじった

Courseraの機械学習の講義も全て受け終わり課題も全て提出が終わりました。

Courseraの講義も受け終わったので次は何か応用面をやろうと思いCNNに手を出してみました。

いろいろと実装が楽と言われているKerasにて実装していろいろ自分で分析をしてみました。

今回使ったのはKeras , matplotlib , Jupyter Lab。

データセットはMNISTを使いました。

CNNのモデルはKerasリポジトリのexamplesにあるものをそのまま使いました。

examplesにあるCNNのコード

github.com

MNISTとは

MNISTは28x28ピクセルの手書き数字が書かれた小さめな画像が大量にあるデータセットです。

60k個のtraining set,10k個のtest setがあります。

トレーニングセットで使うのは60k個の方です。

とりあえずデータの一部を見てみましょう。

f:id:abc10946:20180521125904p:plain

CNNのモデル

今回使うCNNのモデルはこんな感じのです。

これはKerasリポジトリのexamplesにあるシンプルなもので畳み込み層が2つしかないものです。

もっと高度はモデルであるVGG16とかは畳み込み層が13個あります。

f:id:abc10946:20180521130133p:plain

これだけでも結構いい精度を出してくれます。

結果

学習させて認識率(accuracy)を見てみると98.86%ととても良い認識率であることが分かります。

CNNはフィルターという小窓を用いて画像のある程度の範囲をひとまとまりにして見ています。

そのため位置が近いピクセルでは色(今回は黒の濃淡の具合)がとても近い値をとっていたり、ある1つの手書き数字ではある範囲に必ずこの形が現れるといった特徴を捕らえやすくなります。

こういった仕組みからCNNは人間と同等または人間よりも上の認識率を持つことができます。

そのため今回のモデルが誤認識した1.14%は人間でも認識が難しいものとなります。

それをいくつか見ていきましょう。

正解:4

認識結果:2

f:id:abc10946:20180521131558p:plain

正解:9

認識結果:8

f:id:abc10946:20180521131608p:plain

正解:9

認識結果:8

f:id:abc10946:20180521131613p:plain

正解:6

認識結果:0

f:id:abc10946:20180521131617p:plain

これくらいなら人間でも読みにくいと思える字です。

Kerasを使ってみて

Tensorflowを使うよりも驚くほど簡単にモデルを作ることができるのでとても楽です。

KerasでGANの実装とかDQN実装とかしている記事もあるのでそれを参考に画像生成、強化学習あたりにも手を出していく所存です。

参考文献

Keras Documentation

ICTSC本戦行ってきました。

ICTトラブルシューティングコンテストにチームKogCoderとして本戦出場しました。

会場は大阪にあるさくらインターネット様のオフィスにて行われました。

自分も含めチーム全員初参加だったので1問解けて良い方かなと消極的な考えでしたが、思ったよりも問題が解けてよかったです。

自分が解いた問題または点を得るために手伝った問題は

  • アップルパイが完成しない

  • ぼよよんのみ

の2問でぼよよんのみは完答ではありませんでした。

それぞれの問題は以下の様なものです。

アップルパイが完成しない

ラズパイにsshを建てて設定したいけどディスプレイ、キーボード、マウスがないからなんとかする問題。

ラズパイの固定IP、デフォルトゲートウェイ、DNSのIPが指定されているのでそれをsshで接続して設定を行う。

OSはraspbianの最新バージョンで、raspbianの仕様で/bootにsshという名の空ファイルを作ることでsshサーバがraspbian上で起動する。

あとはarp -aでラズパイのIPを見つけてsshで接続し指定された設定を行った。

ぼよよんのみ

ゲームが起動しないからそれをなんとかする、あと脆弱性をできるだけ多く見つける問題。

この問題の脆弱性を突くプログラムを書いてました。

ぼよよんのみ脆弱性攻撃コード

プレイヤーの新規登録、任意プレイヤーへの攻撃、任意プレイヤーの任意座標への転移などができます。

ローカル環境ではうまくいったのですが本番環境ではうまく動かずじまいでした。

Unity備忘録1-光関係

Unity-2017.3で光源関係を使う際のメモ。

下の画像のような光源をUnityで実装する。

f:id:abc10946:20180217233932p:plain

手順

Materialの設定で以下のようにEmissionを有効にしてColorプロパティの値を1より大きい値にする。

f:id:abc10946:20180217234229p:plain

Post-processing profileを作り設定をする。

f:id:abc10946:20180217235621p:plain

CameraにPost Processing Behaviorスクリプトコンポーネントを配置しProfileに設定したPost-processing profileを割り当てる。

f:id:abc10946:20180217235659p:plain

参考

docs.unity3d.com

docs.unity3d.com