Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード

2016-06-15

【iOS 10】Speechフレームワークで音声認識 - 対応言語リスト付き

iOS 10のドキュメントが公開された当日に書いた下記記事で、最も反響が大きかったのが音声認識APIでした。


今回公開された SiriKit(Intents / IntentsUI)とは別のフレームワークSpeech Framework として公開されたものです。リアルタイム音声にも、録音済み音声にも使えるようです。


f:id:shu223:20160616104103j:image


今までも色々と音声認識を実現する手段はありましたが、やはりApple純正となると一気に本命になってきます。*1


というわけで本記事では Speech フレームワークを色々いじってみて、何ができるのかとか、どうやるのかとか見てみたいと思います。


なお、NDA期間中につき、スクショは自粛します。


まずはサンプルを動かしてみる

"SpeakToMe: Using Speech Recognition with AVAudioEngine" というサンプルコードが公開されているのでまずは試してみます。


アプリが起動してすぐ、Speech Recognition の利用許可を求めるダイアログ が出てきます。そんなパーミッションも追加されたんだ、と思って [Settings] > [Privacy] を見てみると、確かに [Speech Recognition] の欄が追加されています。


録音を開始するボタンを押すだけで認識スタンバイOKです。試しに Hello と話しかけてみると・・・"How do" という結果が・・・気を取り直してもう一度 Hello と言ってみると・・・"I don't"・・・


これは自分の英語力の問題なので、諦めてターミナルから `say` コマンドで Hello と言わせると "Hello" と正しく認識してくれました。同時に過去の "I don't" とかも "Hello" に修正されたので、その後の入力からの認識結果によって過去の認識結果も修正するっぽいです。


サンプルの実装を見てみる

SFSpeechRecognizerの準備

初期化して、

private let speechRecognizer = SFSpeechRecognizer(locale: Locale(localeIdentifier: "en-US"))!

デリゲートをセット

speechRecognizer.delegate = self

音声認識の利用許可を求める
SFSpeechRecognizer.requestAuthorization { authStatus in
    OperationQueue.main().addOperation {
        switch authStatus {
            case .authorized:
                // 許可された

            case .denied:
                // 拒否された

            case .restricted:
                self.recordButton.isEnabled = false
                self.recordButton.setTitle("Speech recognition restricted on this device", for: .disabled)

            case .notDetermined:
                self.recordButton.isEnabled = false
                self.recordButton.setTitle("Speech recognition not yet authorized", for: .disabled)
        }
    }
}

コールバックはメインスレッドで呼ばれているとは限らないので `OperationQueue.main().addOperation` で実行しているようです。


認識リクエストの準備

プロパティを定義しておいて、

private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?    

認識開始前に初期化。

recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
guard let recognitionRequest = recognitionRequest else { fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object") }

このプロパティをセットすると、録音が終わる前の "partial (non-final)" な結果を報告してくれる、とのことです。

recognitionRequest.shouldReportPartialResults = true

つまり、確定前の結果を取得できるようで、先ほどの、過去に戻って認識結果が修正される挙動はこのプロパティによるものではないかと思われます。リアルタイム認識時には有効にしておきたい重要プロパティですが、デフォルトでは `false` とのことです。


認識タスクの登録

SFSpeechRecognizer の `recognitionTask:with:resultHandler:` メソッドに SFSpeechRecognitionRequest オブジェクトと、結果を受け取ったときの処理を渡します。

recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
    var isFinal = false
    
    if let result = result {
        self.textView.text = result.bestTranscription.formattedString
        isFinal = result.isFinal
    }
    
    if error != nil || isFinal {
        self.audioEngine.stop()
        inputNode.removeTap(onBus: 0)
        
        self.recognitionRequest = nil
        self.recognitionTask = nil
        
        self.recordButton.isEnabled = true
        self.recordButton.setTitle("Start Recording", for: [])
    }
}

戻り値として、 SFSpeechRecognitionTask オブジェクトが返ってきます。このサンプルでは、認識結果は SFSpeechRecognitionResult オブジェクトの `bestTranscription` プロパティより得ています。


録音開始(認識開始)

ここは AVAudioEngine の機能であって、iOS 8 からあるものなので、省略します。AVAudioEngine については下記記事にも書いたのでよろしければご参照ください。(残念ながら録音については書いてないのですが。。)


唯一 Speech フレームワークと直接関係するポイントは以下。

inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
    self.recognitionRequest?.append(buffer)
}

これで、マイクから得られる音声バッファが SFSpeechRecognitionRequest オブジェクトに渡されるようになり、録音開始と共に認識が開始されることになります。


対応言語は58言語!

基本的な使い方がわかったところで、以下ではサンプルに触れられてない部分も色々と見てみます。まずは、対応言語から。


SFSpeechRecognize は初期化時に以下のように Locale を指定できるようになっています。

private let speechRecognizer = SFSpeechRecognizer(locale: Locale(localeIdentifier: "en-US"))!

`supportedLocales()` というメソッドがあるので、全部出力してみます。

SFSpeechRecognizer.supportedLocales().enumerated().forEach {
    print($0.element.localeIdentifier)
}

出力結果:

nl-NL
es-MX
zh-TW
fr-FR
it-IT
vi-VN
en-ZA
ca-ES
es-CL
ko-KR
ro-RO
fr-CH
en-PH
en-CA
en-SG
en-IN
en-NZ
it-CH
fr-CA
da-DK
de-AT
pt-BR
yue-CN
zh-CN
sv-SE
es-ES
ar-SA
hu-HU
fr-BE
en-GB
ja-JP
zh-HK
fi-FI
tr-TR
nb-NO
en-ID
en-SA
pl-PL
id-ID
ms-MY
el-GR
cs-CZ
hr-HR
en-AE
he-IL
ru-RU
de-CH
en-AU
de-DE
nl-BE
th-TH
pt-PT
sk-SK
en-US
en-IE
es-CO
uk-UA
es-US

なんと、58言語もあります。もちろん日本語 "ja-JP"もサポート。


長文の認識精度

英語で長文を入れてみてもそもそも自分の発音が、というところがあるので、日本語で試してみます。

private let speechRecognizer = SFSpeechRecognizer(locale: Locale(localeIdentifier: "ja-JP"))!

上記の一文をしゃべって入力してみました。今現在フードコートのテーブルでこれを書いてるので、周りはそこそこ騒がしいです。一発勝負で結果は以下でした。


英語で長文を入れてみても様々自分の発音がどういうところがあるので日本語で試してみます


なんと、間違った箇所は「そもそも」 → 「様々」(さまさま?)だけ


なかなか優秀なんじゃないでしょうか。


パフォーマンス

ちゃんと測ってないですが、体感としてはiOSに従来からある音声入力と同じぐらいです。リアルタイムからほんのちょっと遅れて結果が確定していく感じです。


SFSpeechRecognitionResult

認識結果として SFSpeechRecognitionResult オブジェクトが得られるわけですが、公式サンプルで用いられている `bestTranscription` は最も信頼度(confidence)の高い認識結果で、その他の候補も `transcriptions` プロパティより取得することができます。

@property (nonatomic, readonly, copy) SFTranscription *bestTranscription;
@property (nonatomic, readonly, copy) NSArray<SFTranscription *> *transcriptions;

録音済みファイルの認識

SFSpeechAudioBufferRecognitionRequest の代わりに、SFSpeechURLRecognitionRequest クラスを利用します。どちらも SFSpeechRecognitionRequest のサブクラスです。


実装して試してはないのですが、バッファの代わりにファイルのURL(ローカル/オンライン)を指定するだけで、あとは上述の方法と同じかと思われます。

public init(url URL: URL)    
public var url: URL { get }

(追記)WWDCのセッションで言ってたこと

  • Siri および 音声入力機能と下回りは同じものを使用

Same speech technology as Siri and Dictation

  • 自動でユーザーに適応する
  • 基本的には要インターネット
    • 例外あり(some some languages and device models)

後で調べる

  • シミュレータでも実行できるのか?

*1:古い話かつ余談ですが、音声合成も以前色々と比較してみて結局 AVSpeechSynthesizer が良い、という結論に落ち着いたことがありました:http://qiita.com/shu223/items/223492e4f061032e652e:title:bookmark

2015-12-04

Classic Bluetooth について iOS アプリ開発者ができること

Bluetooth Low Energy については Core Bluetooth で色々と制御できますが、Classic Bluetooth(以降クラシックBT)については基本的に開発者は制御できません*1


f:id:shu223:20151205180842p:image:w200


そう、確かにアプリ内からクラシックBTデバイスと接続したり、データを送るとか送らないとか制御したり、通信を切断したり、といったことはできないのですが、そんな中でもアプリ側から一切クラシックBTデバイスの存在を感知することができないのかというとそうでもなく、いくつかの「口」はあります。


というわけでそういう「口」を集めてみました。他にもあればぜひ教えてください!


ボタン操作イベントの取得

BTイヤフォンの再生・停止等のボタン操作イベントを取得するには、次のように取得開始を宣言し、かつファーストレスポンダになります。

UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
self.becomeFirstResponder()

イベントが飛んで来ると、`remoteControlReceivedWithEvent:` が呼ばれます。

override func remoteControlReceivedWithEvent(event: UIEvent?) {
    guard event?.type == .RemoteControl else { return }
    
    if let event = event {

        switch event.subtype {
            
        case .RemoteControlPlay:
            print("Play")
        case .RemoteControlPause:
            print("Pause")
        case .RemoteControlStop:
            print("Stop")
        case .RemoteControlTogglePlayPause:
            print("TogglePlayPause")
        case .RemoteControlNextTrack:
            print("NextTrack")
        case .RemoteControlPreviousTrack:
            print("PreviousTrack")
        case .RemoteControlBeginSeekingBackward:
            print("BeginSeekingBackward")
        case .RemoteControlEndSeekingBackward:
            print("EndSeekingBackward")
        case .RemoteControlBeginSeekingForward:
            print("BeginSeekingForward")
        case .RemoteControlEndSeekingForward:
            print("EndSeekingForward")
        default:
            print("Others")
        }
    }
}

ただし、オーディオセッションカテゴリによっては、イベントがアプリまで飛んでこなくなります(OSでは拾っているが、アプリまで渡してくれない)。試したところでは、`AVAudioSessionCategoryPlayback` では取得可、`AVAudioSessionCategoryPlayAndRecord` では取得不可。


MPRemoteCommandCenter

上記の方法は今でも deprecated になってはいないものの、リファレンスによると、

In iOS 7.1 and later, use the shared MPRemoteCommandCenter object to register for remote control events. You do not need to call this method when using the shared command center object.

と、iOS 7.1 以降では `MPRemoteCommandCenter` を使うように書かれています。


MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetUsingBlock:^(MPRemoteCommandEvent *event) {
    // Begin playing the current track.
    [[MyPlayer sharedPlayer] play];
}

参考: Remote Control Events - Event Handling Guide for iOS


要注意!

記事を書きながら、念のため手元でシンプルなデモをつくって試したところこれがなぜか動作しない・・・!(BTイヤフォンのボタンイベントが飛んでこない)以前、試したときは動いたのに・・・


もちろんファーストレスポンダになるのを忘れる、というよくあるハマりポイントはクリアしています。過去に試したときと条件を合わせるため、同じBTデバイスを使い、iOS 8 で、同じオーディオセッションカテゴリ(Playback)を用いてもダメ。


で、ふと気付きました。


システムの音楽プレイヤーがボタンイベントを捕まえている状況だとアプリまでイベントが飛んで来ないのではないかと。ここでいう「システムの音楽プレイヤー」とは、iOS標準のミュージックアプリおよび `[MPMusicPlayerController systemMusicPlayer]` でアクセスできるプレイヤーのことです。


そこで AVAudioPlayer を使ってアプリ内で音楽を再生しつつクラシックBTデバイスのボタンを操作してみたところ・・・動作しました!


というわけで上述の仮説で合っているようです。


接続/切断をフック

これもイヤフォンやヘッドセット等のオーディオデバイス限定にはなりますが、そういったデバイスが接続/切断されるとき、オーディオの入力・出力ルート(iOS の世界では Audio Route と呼ばれる)の変更通知 `AVAudioSessionRouteChangeNotification` が飛んできます。


というわけでこれを監視しておき、

NSNotificationCenter.defaultCenter().addObserverForName(
    AVAudioSessionRouteChangeNotification,
    object: nil,
    queue: nil) { (notification) -> Void in
        
        // 通知を受け取ったときの処理
}

この状態でイヤフォンやヘッドセットをつなぐと、オーディオルート変更通知が飛んでくるので、そこで今のオーディオルートの入力ポートと出力ポートがそれぞれ何なのか、ということを知ることができます。

let route  = AVAudioSession.sharedInstance().currentRoute
let inPort  = route.inputs.first
let outPort = route.outputs.first

ここで、`AVAudioSessionPortDescription` の `portType` からそのポートのタイプを取得でき、たとえば A2DP プロファイルのBTイヤフォンを繋いだ場合は、`portType` は `AVAudioSessionPortBluetoothA2DP` (中身の文字列は `BluetoothA2DPOutput`)に、HFP プロファイルのBTヘッドセットを繋いだ場合は `AVAudioSessionPortBluetoothHFP` になるので、これを用いればクラシックBTデバイスの接続・切断をフックできることになります。


ただ、この方法も万能ではなくて、音楽再生中とか、録音中とか、オーディオデバイスが機能している状態じゃないと変更通知が飛んできません。(詳細な条件は要調査)


いつでもどうしても確実にフックしたい場合は `currentRoute` をポーリングするとかになるのかと思います(が、やったことないのでこれも何か思わぬ落とし穴があるかもしれません)。


接続デバイス名の取得

前項にて現在のオーディオルート情報 `AVAudioSessionRouteDescription` を取得し、そこから入力/出力ポートの情報を保持する `AVAudioSessionPortDescription` オブジェクトを取り出しました。


この `portName` プロパティから、デバイス名を取得できます。

let route  = AVAudioSession.sharedInstance().currentRoute
let outPort = route.outputs.first
if let outPort = outPort {
    print("out port name:\(outPort.portName)")
}

(BTイヤフォン接続時の実行結果)

out port name:Jabra Rox Wireless v2.5.4

(これ以外に、`UID` というプロパティもありますが、`xx:xx:xx:xx:xx:xx-tacl` とMACアドレスらしきものが入っている場合があったり、単に `Wired Headphones` と入っている場合があったりで、統一性がなく何かの判別には使いづらいかなと感じました)


入力・出力デバイスの選択

`AVAudioSessionRouteDescription` は `inputs`、 `outputs` と、複数の入出力ポートを持てるようになっています。AVAudioSession に `setPreferredInput:` というメソッドがあるので、入力デバイスは明示的に選択できそうです。

/* Select a preferred input port for audio routing. If the input port is already part of the current audio route, this will have no effect.
   Otherwise, selecting an input port for routing will initiate a route change to use the preferred input port, provided that the application's
   session controls audio routing. Setting a nil value will clear the preference. */

public func setPreferredInput(inPort: AVAudioSessionPortDescription?) throws

ただ `setPreferredOutput:` というメソッドは見当たらないので、出力デバイスが複数ある場合はどうするんでしょうか。inputと連動するのか、別のメソッドがあるのか。まだこのあたりは試したことないので、今度挙動を確かめてみます。


つづく

AVAudioSession のヘッダを見ているとまだありそうなので、引き続き何か分かり次第追記していきます。


*1:MFiプログラム締結時(External Accessory framework 利用可能時)は例外

2015-06-24

【iOS9】Audio Unit Extensions 〜オーディオエフェクトのアプリ間共有〜

昨日開催された「WWDC2015報告共有会@ネクスト」にて、iOS 9 で追加された新しい Extension Point のひとつ、「Audio Unit Extensions」についてLTをさせていただきました。



概要

Audio Unit とは?
  • Core Audio においてもっとも低レベル(ハードより)に位置するフレームワーク
  • 低遅延での音声処理が可能
  • ユニット同士を繋げて複雑なオーディオ処理を実現可能
  • iOS 8 より AVFoundation に AVAudioEngine が追加され、Audio Unit の利用ハードルがグッと下がった

Audio Unit Extensions とは?
  • iOS 9 で追加された Extension Point のひとつ
  • Audio Unit をアプリ間で共有できる

→ つまり自作エフェクトや他社製エフェクトを繋げてこういうこともできるようになる!


f:id:shu223:20150624072335j:image:w600

(※写真はイメージです)


  • Audio Unit にはエフェクト以外にもさまざまなタイプがある
    • GarageBand では Audio Unit Extensions の楽器を利用できるようになるらしい。
var kAudioUnitType_Output: UInt32 { get }
var kAudioUnitType_MusicDevice: UInt32 { get }
var kAudioUnitType_MusicEffect: UInt32 { get }
var kAudioUnitType_FormatConverter: UInt32 { get }
var kAudioUnitType_Effect: UInt32 { get }
var kAudioUnitType_Mixer: UInt32 { get }
var kAudioUnitType_Panner: UInt32 { get }
var kAudioUnitType_Generator: UInt32 { get }
var kAudioUnitType_OfflineEffect: UInt32 { get }
var kAudioUnitType_MIDIProcessor: UInt32 { get }

Audio Unit Extensions の利用方法

WWDC のサンプルコード "AudioUnitV3Example" に入っている "FilterDemo" というアプリには、Audio Unit Extensions が含まれています。まずはこのアプリをインストールして、「エクステンション利用側」を実装してみます。


利用可能なユニットのリストを取得する

iOS 9 で追加されたクラス AVAudioUnitComponentManager の `componentsMatchingDescription:` メソッドを呼ぶと、

var anyEffectDescription = AudioComponentDescription()
anyEffectDescription.componentType = kAudioUnitType_Effect
anyEffectDescription.componentSubType = 0
anyEffectDescription.componentManufacturer = 0
anyEffectDescription.componentFlags = 0
anyEffectDescription.componentFlagsMask = 0

availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager()
    .componentsMatchingDescription(anyEffectDescription)

利用可能な Audio Unit の AVAudioUnitComponent (ユニットのタイプや制作者などの情報が入っているクラス)のリストが得られます。


この中に、インストールした"FilterDemo"サンプルが contain しているユニット "FilterDemo" も入ってきます。


ユニットを適用する

以下の手順で、所望の Audio Unit をサウンドに適用します。

  1. AVAudioUnitComponent の AudioComponentDescription (構造体)を取得する
  2. AVAudioUnit を `instantiateWithComponentDescription:options:` で生成する(このメソッドは iOS 9 で新規追加)
  3. AVAudioEngine に attachNodeする
  4. エフェクトノードと、プレイヤー・ミキサー等のノードを `connect:to:format:` で接続する

※AVAudioEngine を用いて Audio Unit のエフェクトをかける基本的なサンプルは、iOS8-Sampler にも入っているので、よろしければご参考に。


f:id:shu223:20150624072630j:image:w200

(iOS8-Sampler の "Audio Effects" サンプル)


Audio Unit Extensions の作成方法

なんと、Xcode 7 beta には Audio Unit Extensions を作成するためのテンプレートがまだありません(という話は WWDC セッション内でも話されています)。とりあえず現状では "FilterDemo" からコピペして使ってくれ、とのこと。


Audio Unit の関連記事


2014-12-12

iOSと機械学習

ビッグデータとかの機械学習隆盛の背景にある文脈や、その拠り所となるコンピュータの処理性能から考えても「モバイルデバイス向けOSと機械学習を紐付けて考えようとする」ことはそもそもあまり筋がよろしくない・・・とは思うのですが、やはり長くiOSだけにコミットしてきた身としては、新たに興味を持っている機械学習という分野と、勝手知ったるiOSという分野の交差点はないのかなと考えずにはいられないわけでして。。


そんなわけで、「iOS と機械学習」について雑多な切り口から調べてみました。


iOSで使える機械学習ライブラリ

DeepBeliefSDK

コンボリューショナルニューラルネットワークを用いた画像認識ライブラリ。iOSとかのモバイルデバイスで処理できるよう、高度に最適化してある、OpenCVと一緒に使うのも簡単、とのこと。


何はともあれ SimpleExample というサンプル実行したら、


f:id:shu223:20141211210821j:image


頼んでもないのにいきなりノートパソコンを認識しました!すごい!


SimpleExample のソースを見てみると、フレーム毎に得られるピクセルバッファの処理はこんな感じでした。

- (void)runCNNOnFrame: (CVPixelBufferRef) pixelBuffer
{
  assert(pixelBuffer != NULL);

	OSType sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer );
  int doReverseChannels;
	if ( kCVPixelFormatType_32ARGB == sourcePixelFormat ) {
    doReverseChannels = 1;
	} else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat ) {
    doReverseChannels = 0;
	} else {
    assert(false); // Unknown source format
  }

	const int sourceRowBytes = (int)CVPixelBufferGetBytesPerRow( pixelBuffer );
	const int width = (int)CVPixelBufferGetWidth( pixelBuffer );
	const int fullHeight = (int)CVPixelBufferGetHeight( pixelBuffer );
	CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
	unsigned char* sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer );
  int height;
  unsigned char* sourceStartAddr;
  if (fullHeight <= width) {
    height = fullHeight;
    sourceStartAddr = sourceBaseAddr;
  } else {
    height = width;
    const int marginY = ((fullHeight - width) / 2);
    sourceStartAddr = (sourceBaseAddr + (marginY * sourceRowBytes));
  }
  void* cnnInput = jpcnn_create_image_buffer_from_uint8_data(sourceStartAddr, width, height, 4, sourceRowBytes, doReverseChannels, 1);
  float* predictions;
  int predictionsLength;
  char** predictionsLabels;
  int predictionsLabelsLength;

  struct timeval start;
  gettimeofday(&start, NULL);
  jpcnn_classify_image(network, cnnInput, JPCNN_RANDOM_SAMPLE, 0, &predictions, &predictionsLength, &predictionsLabels, &predictionsLabelsLength);
  struct timeval end;
  gettimeofday(&end, NULL);
  const long seconds  = end.tv_sec-- start.tv_sec;
  const long useconds = end.tv_usec - start.tv_usec;
  const float duration = ((seconds) * 1000 + useconds/1000.0) + 0.5;
  NSLog(@"Took %f ms", duration);

  jpcnn_destroy_image_buffer(cnnInput);

  NSMutableDictionary* newValues = [NSMutableDictionary dictionary];
  for (int index = 0; index < predictionsLength; index += 1) {
    const float predictionValue = predictions[index];
    if (predictionValue > 0.05f) {
      char* label = predictionsLabels[index % predictionsLabelsLength];
      NSString* labelObject = [NSString stringWithCString: label];
      NSNumber* valueObject = [NSNumber numberWithFloat: predictionValue];
      [newValues setObject: valueObject forKey: labelObject];
    }
  }
  dispatch_async(dispatch_get_main_queue(), ^(void) {
    [self setPredictionValues: newValues];
  });
}

・・・と決してシンプルとは言い難いですが、ここで行っている分類処理のコアは、

jpcnn_classify_image(network, cnnInput, JPCNN_RANDOM_SAMPLE, 0, &predictions, &predictionsLength, &predictionsLabels, &predictionsLabelsLength);

この1行にあるのかなと。で、あとはバッファまわりの諸々とか、得られた結果の処理とか。


おもしろそうなので、また別の機会にちゃんと見てみようと思います。


ML4iOS

iOSでの機械学習を行うためのオープンソースライブラリ。ライセンスは Apache License, Version 2.0 。


コミット履歴を見ると、3年前(2012年1月)からあり、つい最近も更新されています。


LearnKit

iOS, OS X 向け機械学習フレームワーク、とのこと。


サポートしているアルゴリズム

  • Anomaly Detection
  • Collaborative Filtering
  • Decision Trees
  • k-Means
  • k-Nearest Neighbors
  • Linear Regression
  • Logistic Regression
  • Naive Bayes
  • Neural Networks
  • Principal Component Analysis

READMEに下記のようなサンプルコード載ってるので、今度試す。

LNKNeuralNetClassifier *classifier = [[LNKNeuralNetClassifier alloc] initWithMatrix:matrix 
                                                                 implementationType:LNKImplementationTypeAccelerate
                                                              optimizationAlgorithm:algorithm
                                                                            classes:[LNKClasses withRange:NSMakeRange(1, 10)]];
[classifier train];

LNKClass *someDigit = [classifier predictValueForFeatureVector:someImage length:someImageLength];

使いやすそう。


mlpack-ios

mlpack を Objective-C プロジェクトにリンクできるようにしたもの、とのこと。


mlpack というのは「スケーラブルなC++機械学習ライブラリ」らしい。


Swift-Brain

Swiftで書かれた人工知能/機械学習ライブラリ。ベイズ理論、ニューラルネットワーク、その他AIが実装されているとのこと。


  • Matrices
    • Matrix operations
  • Machine Learning algorithms
    • Basic Regressions
    • Neural Networks
    • Support Vector Machines
    • Bayesian Classifiers
    • Self Organized Maps (maybe?)
    • Clustering
  • Statistics
    • Bayes Theorem/Naive Classifier
    • Kalman Filter
    • Markov Model

学習結果をiOSで利用

機械学習によって得られたモデル等をiOSで利用する」ケースです。


冒頭で、「iOSと機械学習を結びつけて考えるのはあまり筋がよろしくない」と書きましたが、このケースでは学習自体はiOSデバイスではなくバックエンド(という表現が正しいかは不明)で行うので、至極真っ当、というか王道かと。


画像認識

OpenCV for iOS に機械学習で作成したモデル(分類器)のxmlファイルをアプリに持たせれば、人の顔以外にもいろんなものを認識できますよ、という記事。



分類器自体はプラットフォーム依存ではないので、iOSという限定を外せばかなり色々とチュートリアル記事とかモデル配布記事がでてきます。


文字認識

iOS で使える OCR ライブラリ。


機械学習で精度を向上させたり、日本語を覚えさせたり。


参考:no title


音声認識

こういう話でいえば、同様に、iOS用の音声認識エンジンも学習データを用意してモデルを自作することができます。

OpenEars は PocketSphinx (CMU Sphinx) というカーネギーメロン大学によるオープンソース認識エンジンのラッパーライブラリで、それ用の音響モデルを自作することはできます(とある案件で試したことあり)。


国産の「大語彙連続音声認識エンジン Julius」(iOSで利用可能)も、もちろん学習によりモデルを自作できます。


実例集

ストアに出ているアプリなど。


Deep Belief by Jetpac - teach your phone to recognize any object

上でも紹介した、DeepBeliefSDKを使ったアプリ。


f:id:shu223:20141211211050j:image:w200


このアプリでネコを認識するデモ動画。


Deep Learning

ディープラーニングで画像を分類するアプリ。


f:id:shu223:20141211211118j:image:w200


Summly

2011年12月と、ちょっと古い記事ですが、Summlyという「ウェブのコンテンツを箇条書きとキーワードの一覧に要約する」アプリのニュース記事。

まずは、特別なアルゴリズムでHTML処理を使って、ウェブページからテキストを抜き出すことから始まる。そのテキストを分析して、記事から選び出された「凝縮された部分」を箇条書きで吐き出す。Summlyのアルゴリズムは、いくつもの機械学習の手法と「遺伝的」アルゴリズム――進化をまねた発見的探索法――を利用してこれを行っている。

ダロイジオ氏のアルゴリズムは、さまざまな出版社によるいろいろなタイプの記事の、人間による要約を調査した。そしてこれらの要約を、Summlyが吐き出すべきものや、情報キュレーションを行う人間の仕事をうまく真似るための、メトリクス[尺度]を調整する際のモデルとして活用した。


その後 Yahoo! に買収され、Yahoo!のニュース要約とパーソナライズ表示、動画、画像検索機能等にその技術が用いられているとのこと。


Plant Recognition: Bringing Deep Learning to iOS

ディープラーニングで植物を認識する、という論文。


f:id:shu223:20141211211217j:image


おわりに

iOS と機械学習を絡めたお仕事、お待ちしております!


2014-11-25

新しい楽器をつくるハッカソン「Play-a-thon」に参加してきました

つい10日ほど前に TechCrunch ハッカソンに参加した件について書いたばかりですが、実は今月の6(木)、7(金)、21(金)、24(月)と何と4日間、しかも平日(!)*1 という日程で、「新しい楽器をつくる」というコンセプトのハッカソン、その名も「Play-a-thon」に参加してきました。


参加のいきさつ

10月のある日、珍しく IAMAS の小林先生から Facebook Message が届き「これは何か面白い仕事の予感・・・!」と思って読んでみるとこのPlay-a-thonへのお誘いでした。


すごーく興味をそそられたものの、

  • 日単位で時間(と技術)を切り売りしているフリーランスエンジニアという立場上、n日の予定を入れると n × {日単価} の出費と実質的に同等
  • 当時既に同じ11月に開催されるTechCrunchハッカソンへの特別参加エンジニアとしての参加も決定していた。1ヶ月に合計6日間ハッカソン。。
  • 執筆含め既に予定がいっぱいいっぱいだった

という事情があり一瞬お断りしようと思ったのですが、「平日4日間拘束」という妙に高い参加ハードルが逆にコアな人たちを集めそうでおもしろそうという期待と、小林先生直々の勧誘なのでできれば乗っかりたいという気持ちとで、気付くとPeatixでチケットお買い上げしていたのでした。


内容

Engadget さん主催ということで、詳しいことは近々アップされるであろうレポート記事をご覧いただくとして、ざっくり書くと

  • 11/6:工場見学・懇親
  • 11/7:アイデア出し、チーム作り、プロトタイピング(廃材で工作)
  • 11/21:制作
  • 11/24:Engadget Fesにて発表(つくった楽器でライブ演奏!)

こんな日程でした。


つくったもの

「食べられる楽器」というコンセプトで、日本全国どこでも10円で手に入る「うまい棒」を使った楽器をつくりました。


f:id:shu223:20141126093948j:image:w400


うまい棒+トロンボーンで『ウマイボーン』と呼びます。マウスピース部分とスライド部分がうまい棒になっていて、トロンボーンと同様スライドうまい棒を上下に動かすことで音程が変わります


Umaibone Demo1

(ライブ前のお披露目会にてチラッと音出し、の様子)


Umaibone LIVE 1

(ライブ冒頭の独演。実際にうまい棒を操作して生演奏してます)


しくみ

しくみを先に読んでしまうとあまり楽しくない(しくみ自体に発明感はない)ので、あえて控えておこうと思います。


が、ひとつだけ書いておきますと、ユカイ工学さんの「Konashi」を使用しております。


konashiはひさびさに触ったのでこちらの(以前自分が書いた)記事を参考に思い出しつつ実装しました。


ちなみに前回konashiを触ったのは8月ぐらいのこのお仕事でした。


余談ですが、ライブ演奏の前日ぐらいに Konashi 2 が発表され、通信機能が強化されつつ安くなった、ということで Maker Faire Tokyo に行った際にユカイさんのブースで直接買おうと思ったのですが発売は12月でした。。orz


(konashi 2 の変更点)

  • PIOのピン数の変更(8->6)
  • DAC(アナログ出力)未対応
  • UARTの対応baudrateを追加
  • UARTで複数バイトを一度に送受信を可能に
  • I2Cで一度に送受信できるバイト数を、20から16に変更
  • BLEのService,CharacteristicのUUIDを変更
  • PWMを出せるピンがPIO0-2の3本のみに変更
  • PWM波形の仕様変更
  • OTAファームウェアアップデートに対応

ちなみに宣伝的な余談が続いて恐縮ですが、初代konashi開発者の松村礼央さんとiOS×BLEな書籍を執筆中ですのでそちらも出たらよろしくお願いします。


変遷

Play-a-thon日程に沿って「ウマイボーン」の進化の過程を記しておきます。


11/7 廃材でのハードウェアスケッチ

f:id:shu223:20141126034502j:image:w360


発泡スチロールを円筒形に削り、ガムテープを巻きつけたもの(うまい棒のつもり*2)と、吹き口としての紙コップ、あとLEDがピカピカ光るみたいなイメージで正体不明の赤い半球をつけたもの。Arduinoはぶら下がってるだけ


11/21 プロトタイプ

f:id:shu223:20141126034557j:image:w600


謎のドアノブ(持ち手のつもり)、測距センサと反射板(紙に白いテープを貼ったもの)、相変わらず発泡スチロールの胴体。うまい棒が文字通り取ってつけてある


一応トロンボーン的にうまい棒スライドで音程を変えられる(こともある)。


11/24 初代完成

f:id:shu223:20141126094141j:image:w300


3Dプリンタで出力された筐体に(konashi以外の)回路が収まり、うまい棒を使った「食べられる楽器」としての体裁がだいぶ整った。

  • うまい棒スライドで音程が連続的に変化
  • 4オクターブの範囲で音域を切り替え可能
  • 楽器の音色切り替え可能(チューバ、トロンボーン、トランペット、オーボエ)
  • 加速度センサによるサンプラー機能(ライブ演出用)
  • マウスピースをかじると発動するスイッチ

といった機能を搭載。


ライブ当日

Umaibone LIVE2

(真ん中のメガネかけてる吉田氏のウマイボーンだけ本物で、実際に生演奏してます)


(フル動画は準備中です)


クレジット

  • デバイス&ウマイボーン演奏:Takatoshi Yoshida(量子物理学専攻の東大生)
  • 作曲&ライブ演出:川瀬浩介(プロの作曲家)
  • ロゴ、Tシャツ、アプリアイコンデザイン:Hisami Takezawa(デザイナー)
  • 筐体制作:Mitsuhiro Hirano(チームラボで日々デバイスつくってる方)
  • プログラム:堤

f:id:shu223:20141126040511j:image:w500

(左から、Yoshidaさん、堤、川瀬さん、Takezawaさん。Hiranoさんはこのときお仕事で欠席。。)


ウマイダーQ(Umaider Quintet)*3のFacebookページ:


主催のEngadgetさん、小林先生、YAMAHAのみなさま、参加者のみなさま、チームのみなさん、どうもありがとうございました!!!!


*1:ただし最終日だけ祝日

*2:会場が山奥だったので買えなかった

*3:当初の案はうまい棒+リコーダーだったので、チーム名に「ウマイダー」と付いています

2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |