micro:bitを使って子供と一緒に夏休みの自由研究をしてみました
夏休みのことなので少し前の話になりますが、小学生の娘と一緒に夏休みの自由研究にmicro:bit を使ったギターを制作しました。
micro:bit って何ですか?
イギリスの BBC が開発した小学生向けの小型コンピュータです。子供向けではありますが、傾きセンサや光センサ、温度センサなんかもついていて本格的です。スピーカーに繋げば音を出すこともできます。Raspberry Pie や Arduino の子供版といった感じです。
マイクロソフトが開発した Scratch ライクな開発環境があるのでブラウザ上で手軽にプログラミングができます。
ギターの制作
小学2年生の娘が工作担当、自分がプログラミング担当といった感じで作りました。本当は娘にプログラミングもやって欲しかったのですが制作期間の関係で私が担当しました。以下の二つの工作例が元ネタです。
ちなみに電源供給とスピーカーは MI:power board で行いました。
ワニ口クリップがあるといろいろ捗ります
mcro:bit から音を出すためにスピーカーに繋いだりするのにワニ口クリップがあると便利です。mcro:bit 単体でも十分遊べるのですがワニ口クリップがあると遊びの幅がグッと広がるのでオススメです。
接着にはグルーガンがおすすめです
ダンボール同士をくっつける時なんかはボンドやノリを使うよりもグルーガンを使った方が丈夫に仕上がります。うちの小学2年生の娘も初めこそ苦戦してましたがそこそこちゃんと使えるようになってました。
参照ページ
数学知識ほぼ0の人間が深層学習の勉強をはじめてみた
きっかけは Magenta プロジェクト
最近巷では機械学習や深層学習(ディープラーニング)などAI系の技術が流行っていますが自分は2-3ヶ月前まで全くと言っていいほど興味がありませんでした。機械学習とかって画像解析や翻訳で使う技術でそのうち頭のいい人がなんとかしてくれるんじゃないのくらいの認識でした。その認識が一変したのが Google Magenta プロジェクト の AI Duet by Yotam Mann - AI Experiments でした。自分も深層学習を使って自動作曲やりたいということで勉強を始めることにしました。これまで自分が勉強したことや読んだ本のことをメモがてらまとめてみます。
勉強をはじめたころのスペック
- Pythonは使ったことがある、ただしnumpyは使ったことがない
- 数学はほどんとわからない(または忘れた)、ゲームの開発をしていたので三角関数くらいはなんとか理解できるくらい
- 機械学習と深層学習の違いがわからない
- 昨今流行っているAIがゲームAIとどう違うのかわからない
自動作曲やるならニューラルネットワークを勉強するのがよさげ
軽くMagenta プロジェクトのことや「自動作曲+AI」みたいなキーワードで調べてみるとニューラルネットワークを理解すると色々できるようになるっぽいということがわかりました。
数学さっぱりだけどニューラルネットワークについて勉強したい
本屋さんでニューラルネットワークの本を探して見ましたがどれも最低限の数学的な知識が必要そうな雰囲気でした。自分は数学が苦手で高校生の頃まともに数学を勉強した記憶がほとんどない....これは敷居が高いどうしようとなりました。
ライブラリを使って学ぶかライブラリ無しで勉強するか
いろいろと調べているうちに TensorFlow と Chainer この2つのライブラリが人気らしいことがわかりました。ただこれらのライブラリのチュートリアル読んでも専門用語がわからなさすぎて使いこなせる気が全くしませんでした。なのでライブラリに頼らず1から自分で実装してみるのがよさそうとなりました。
数学が苦手でも読めるニューラルネットワーク本との出会い
数学が苦手な自分でも内容がわかりライブラリに頼らず1から実装を学べるそんなわがままに答えてくれる本なんてないと思っていたらなんとも自分にぴったりな本を見つけました。
数式はあるものの初心者にもわかるように丁寧に解説してくれている、ニューラルネットワークについての基本的な用語の解説もある、Pythonの実装例まである、知りたいことほとんど載ってるじゃないか...素晴らしすぎる。バイアスの説明が省略されていること以外は必要な知識がほぼ網羅されています。
もっと基本的なところから数学の勉強したい場合は
深層学習の本ではなく機械学習の本ですが、深層学習に比べて数式が簡単なので深層学習の本よんで数式が全く理解できないって場合にオススメです。
ものすごく基本的なところから説明してあってかつPythonの実装例もあるのでかなり理解しやすいです。
深層学習を使ってどんなことができるのか
- 手書文字認識
- 画像の分類
- 自動作曲
- 翻訳なと自然言語処理
覚えた用語
- 入力層
- かくれ層
- 出力層
- 重み
- 誤差逆伝搬
学習って何?
誤差が最小になるような重みの組み合わせを見つけること
深層学習を理解する上で必要な知識
よく出てくる数学用語
スカラー、ベクトル、行列、テンソル
次元 | 名前 | 説明 |
---|---|---|
0次元 | スカラー | 数値 |
1次元 | ベクトル | 配列 |
2次元 | 行列 | 2次元配列 |
3次元以上 | テンソル | 3次元配列以上 |
ただしベクトルには列ベクトルと行ベクトルがある、通常は列ベクトルだけ考えれば良い
ベクトルと行列関連の用語
実装してみる
Pythonの実装例を元にSwiftで実装してみました。 github.com 3層ニューラルネットだけでは物足りず、今はLSTMの実装に挑戦中です。
参考
オブジェクト指向の次に来るもの
自分が駆け出しのプログラマだった頃(10年以上前ですかね)、必死でオブジェクト指向プログラミングの勉強をしました。最近だとオブジェクト指向プログラミングなんて当たり前すぎて技術とすら言えないくらいになったと感じてます。 エンジニアが生き残るために次に必要な技術は何かここ数年いろいろ考えてきましたが、次は間違いなくリアクティブプログラミングだと予想しています。1年半ほど前、UniRxを使ったのをきっかけに現在もRxSwiftを使って日々リアクティブプログラミングと接しています。そんな大事な技術ですがあまり専門書がないのが困ったところです。そんな折に見つけたのが本書です。
厳密にはリアクティブプログラミング(RP)について書かれた本ではなく関数型リアクティブプログラミング(FRP)について書かれた本です。関数型リアクティブプログラミングはこの本によると「関数型プログラミング(FP)とリアクティブプログラミングの交わる部分」だそうです。 FRPの考え方はRx(リアクティブプログラミング)にも応用可能ということなんでこの本を読んで知見をためたいと思います(まだ全部読んでません)。
面倒な課金処理をRx化するRxStoreKitを作りました
StoreKit の SKProductsRequest と SKPaymentQueue を Rx化して課金処理を簡単に書けるライブラリを GitHub にて公開しました。 github.com RxStoreKit を使うと課金処理を以下のような感じで書けます。
let productRequest = SKProductsRequest(productIdentifiers: Set(["xxx.xxx.xxx"])) productRequest.rx.productsRequest .flatMap { response -> Observable<SKProduct> in return Observable.from(response.products) } .flatMap { product -> Observable<SKPaymentTransaction> in return SKPaymentQueue.default().rx.add(product: product) } .subscribe(onNext: { transition in print(transition) }).disposed(by: disposeBag) productRequest.start()
Rx以前の課金処理
だいぶ前の記事ですが iOS アプリの課金処理まわりの記事を書きました。 glassonion.hatenablog.com こちら見ていただくとわかると思いますが StoreKit(In-App Purchase) のプログラムはものすごく複雑です。中でも SKPaymentTransactionObserver の処理が特に大変です。StoreKit のような非同期コールバック地獄みたいなプログラムは RxSwift とRxCocoa を使って Rx化するとものすごく簡略化できます。というわけで RxStoreKit を宜しくお願いします。
関連記事
失敗しない iOS In-App Purchase プログラミング - A Day In The Life
RxSwift DelegateProxy with required methods – LazySequence<UnsafePointer>
Swift3でMIDIファイルをパースする方法
Swift3 で MIDI ファイルのパースをしようとしたら意外に方法が見つからなくて苦労したのでメモです。 MusicEventIteratorGetEventInfo 関数で取得したデータを MIDINoteMessage オブジェクトに変換する方法がわからなくてハマりました。
import AudioToolbox public class MIDIParser { public static func parse(url midiFileUrl: URL) { var musicSequence: MusicSequence? var result = OSStatus(noErr) result = NewMusicSequence(&musicSequence) guard let sequence = musicSequence else { print("error creating sequence : \(result)") return } // MIDIファイルの読み込み MusicSequenceFileLoad(sequence, midiFileUrl as CFURL, .midiType, MusicSequenceLoadFlags.smf_ChannelsToTracks) var musicTrack: MusicTrack? = nil var sequenceLength: MusicTimeStamp = 0 var trackCount: UInt32 = 0 MusicSequenceGetTrackCount(sequence, &trackCount) for i in 0 ..< trackCount { var trackLength: MusicTimeStamp = 0 var trackLengthSize: UInt32 = 0 MusicSequenceGetIndTrack(sequence, i, & musicTrack) guard let track = musicTrack else { continue } MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &trackLength, &trackLengthSize) if sequenceLength < trackLength { sequenceLength = trackLength } var tmpIterator: MusicEventIterator? NewMusicEventIterator(track, &tmpIterator) guard let iterator = tempIterator else { continue } var hasNext: DarwinBoolean = false MusicEventIteratorHasCurrentEvent(iterator, &hasNext) var type: MusicEventType = 0 var stamp: MusicTimeStamp = -1 var data: UnsafeRawPointer? var size: UInt32 = 0 while hasNext.boolValue { MusicEventIteratorGetEventInfo(iterator, &stamp, &type, &data, &size) if type == kMusicEventType_MIDINoteMessage { let messagePtr = UnsafePointer<MIDINoteMessage>(data?.assumingMemoryBound(to: MIDINoteMessage.self)) guard let channel = messagePtr?.pointee.channel, let note = messagePtr?.pointee.note, let velocity = messagePtr?.pointee.velocity, let duration = messagePtr?.pointee.duration else { continue } let message = MIDINoteMessage(channel: channel, note: note, velocity: velocity, releaseVelocity: 0, duration: duration) print(message) } MusicEventIteratorNextEvent(iterator) MusicEventIteratorHasCurrentEvent(iterator, &hasNext) } DisposeMusicEventIterator(iterator) MusicSequenceDisposeTrack(sequence, track) } DisposeMusicSequence(sequence) } }
参考
Objc や Swift2の情報はそこそこありました。
Swift3 のポインタ型についてちゃんと理解してればハマらなかったのかなと反省してます。
ReSwiftのステートをRxSwiftを使って監視する
普段から RxSwift(Reactive ExtensionsのSwift実装)と ReSwift(ReduxのSwift実装)を使っています。RxSwift を通信系の処理だったりデリゲートの処理(例えば StoreKit とか CoreBluetooth)だったりに使って、ReSwiftをアプリ全体で管理しなきゃいけないデータの監視なんかに使ってます。ReSwift をそのまま使ってもあまり困らないのですが、ReSwift のステートを Observable に変換してUI部品とバインドするといろいろと捗るなぁというわけで ReSwift の Rx 用ラッパークラスを作成しました。
ReduxのステートをRx化するクラス
Redux のステートを Rx の Variable に格納して、Redux からステート変更の通知が来たら Variable(エラーの発生しないBehaviorSubject)を更新するクラスを作成します。
import RxSwift import ReSwift class RxStore<AppStateType: StateType>: StoreSubscriber { // 監視する値 let state: Variable<AppStateType> private let store: Store<AppStateType> init(store: Store<AppStateType>) { self.store = store self.state = Variable(store.state) // 購読開始 self.store.subscribe(self) } deinit { // 購読終了 self.store.unsubscribe(self) } func newState(state: AppStateType) { // Variableを更新する self.state.value = state } }
使い方
Store を生成します。ここは通常の ReSwift の使い方そのままです。
// ストア struct AppState: StateType { var count: Int = 0 } // アクション enum AppAction: Action { case incAction } // Reducer struct AppReducer: Reducer { func handleAction(action: Action, state: AppState?) -> AppState { let state = state ?? AppState() return AppState(count: state.count + 1) } } // ストアオブジェクトの生成 let mainStore = Store<AppState>( reducer: AppReducer(), state: nil )
ViewController で Rx化したステートを subscribe(購読)する場合は以下のようになります。
import UIKit import RxSwift import ReSwift class ViewController: UIViewController { let disposeBag = DisposeBag() let rxStore = RxStore<AppState>(store: mainStore) override func viewDidLoad() { super.viewDidLoad() rxStore.state.asObservable() .subscribe(onNext: { state in // 更新された時の処理 print(state.count) }).disposed(by: disposeBag) } } // ディスパッチ mainStore.dispatch(AppAction.incAction) mainStore.dispatch(AppAction.incAction) mainStore.dispatch(AppAction.incAction) mainStore.dispatch(AppAction.incAction)
UI部品とバインド
Rx化されて入れば UI 部品と簡単にバインドできます。
class ViewController: UIViewController { let disposeBag = DisposeBag() @IBOutlet weak var button: UIButton! override func viewDidLoad() { super.viewDidLoad() // カウントが3を越えたらtrueにする let appStateValid = rxStore.state.asObservable() .map { state -> Bool in return state.count > 3 }.shareReplayLatestWhileConnected() // ボタンのEnabledとバインドする appStateValid .bind(to: button.rx.isEnabled) .addDisposableTo(disposeBag) } }
参考記事
enum駆使してKeychainをUserDefaultsっぽく使う
以前、こちらの記事で enum(列挙型)を使って UserDefaults のキー名を管理する方法について紹介しました。この方法を応用(ってほど大袈裟なもんじゃないけど)して Keychain にセキュアなデータを保存してみたいと思います。
Keychainって何?
API アクセスに使う認証用のトークン文字列なんかは UserDefaults じゃなくてもっとセキュアなところに保存する必要があります。そんなときに使うのが Keychain です。詳しい説明は以下の記事が参考になると思いますので以下をご一読ください
Keychainに文字列を保存する
enum に Keychain 用のキー名を列挙してデータ保存と取り出しのためのメソッドを追加します。
import Foundation import Security public enum Keychain: String { // キー名 case accessToken = "accessToken" case password = "password" // データの保存 public func set(_ value: String) { let query: [String: AnyObject] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: self.rawValue as AnyObject, kSecValueData as String: value.data(using: .utf8)! as AnyObject ] SecItemDelete(query as CFDictionary) // エラー処理サボったのでこのへんはよしなに直してください SecItemAdd(query as CFDictionary, nil) } // データの取り出し public func value() -> String? { let query: [String: AnyObject] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: self.rawValue as AnyObject, kSecReturnData as String: kCFBooleanTrue, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == noErr else { return nil } guard let data = result as? Data else { return nil } return String(data: data, encoding: .utf8) } }
使い方
使い方は以下のようになります。
// データの保存 Keychain.accessToken.set("xxxxxx") // データの取り出し if let value = Keychain.accessToken.value() { print(value) // xxxxxxと出力される }
ユニットテスト時の注意点
こちらのコードを XCTest でテストする時は Host Application を指定するのを忘れないでください。 Host Application ってなんだそれって方は以下の記事を参考にしてください
参考記事
- Keychain Services とは
- SwiftでKeychainに保存したデータが取れないときに確認すること
- UnsafeMutablePointer<CFTypeRef> in Swift 3
- Setup Host App for Keychain Tests in Xcode 8