「Magentaで開発 AI作曲 【第3章】」を参考にMelody RNNでスノハレサビ1小節に続くメロディを生成してみた

 

アキバをブラブラしていたら、「Magentaで開発 AI作曲 」という本が目に入った。

気付いたら購入していた。

 

Magentaとは

magenta.tensorflow.org

Google先生の音楽機械学習ライブラリ。

数千曲のmidiデータから学習・作成された学習済みデータを基に、新たなmidiデータを生成する。

モデルによって、単純な単音メロディーはもとより、ドラムパターン、3パートによるバンド曲、コード進行に沿ったアドリブメロディ等を生成できるようだ。

 

章立て

「Magentaで開発 AI作曲」では、下記のような章立てとなっている。

  1. AIで作曲できる音楽機械学習ライブラリMagenta
  2. Magenta環境構築AI作曲実践準備
  3. 単音のシンプルなメロディーをつくるMelody RNN
  4. ドラム演奏の作曲を行うDrums RNN
  5. 3パートのバンド演奏作曲MusicVAE
  6. コード進行に沿ったアドリブメロディーの作曲Improv RNN
  7. バッハ風合唱曲の作曲を行うPolyphony RNN
  8. 現代音楽のような複雑な和音の作曲Pianoroll RNN
  9. 高度なピアノ演奏の作曲Performance RNN
  10. 音楽データの学習方法と学習済みデータの作成
  11. Magenta開発・独自モデルの作成
  12. Appendix Magentaのバリエーション

今回は、
3. 単音のシンプルなメロディーをつくるMelody RNN
を試してみた。

 

Melody RNNについて

Melody RNNは単音メロディを生成する音楽生成モデル。

Melody RNNで使用できる学習済みデータは4種類あり、それぞれ用途・特徴が異なる。

引用:Magentaで開発 AI作曲 P041 表3.1 各学習済みデータの特徴と音域

 

また、パラメータを設定することで、生成する曲数や曲長、テンポ、メロディ生成の基準となる最初の音・メロディを指定することができる。

 

お試し生成

今回は、Melody RNN & 各学習データを用いて、単音メロディーのmidiデータを生成してみた。

パラメータは下記の通り。

 

 

Basic:基本的なメロディー生成

Lookback:パターンに沿ったメロディー生成

 

Attention:時間的整合性のあるメロディー生成

 

Mono:MIDI全音域をカバー

 

おわりに

無事生成できた。

音楽的な良し悪しはよぐわがんにゃい...

歌詞をつけてボカロ等に歌わせるだけで、もしかしたらいい感じになるかもしれない。

次は4章よ~

ジングルベルがとまらない チャレンジ 備忘録

はじめに

 2021年12月25日、スクスタからのクリスマスプレゼント(爆弾)として「ジングルベルがとまらない」のチャレンジ難易度が配信されました。ユメノトビラを彷彿とさせる強烈な殺意を感じさせるものでしたが、無事Sランククリアの目処が立ったので、備忘録的に方針を書き殴ろうと思います。

※2021/12/26時点(実装2日目)で以下のような感じ。

f:id:skty001:20211226212933p:plain

特徴・タイムライン

曲の特徴は以下の通り。

  • ピュア以外に強烈な基本デバフ
  • 3万ダメージ10回、10万ダメージ1回、シールド貫通50%ダメージ2回
  • 50ノーツ続くスタミナ回復不可デバフ中に9万ダメージが2回
  • 達成時大ダメージのSPAC
  • 曲開始直後の10万+3万ダメージ
  • 目標Voがかなり厳しめに設定されたVoAC
  • ノーツのダメージは抑えめ

f:id:skty001:20211226200411p:plain

※おそらく誤りがあります。30000ダメージと100,000ダメージがアイコンで区別できんのや...

 大ダメージの嵐という面ではユメノトビラと似ているが、ユメノトビラは軽減前提(素で受けることを考慮しない)の大ダメージであるのに対して、ジングルベルがとまらないは軽減すべき大ダメージと素で受ける(受けざるを得ない)大ダメージが存在し、捌き分ける必要がある点で異なる。

前提

本攻略は以下の特徴を持つキャラクターに強く依存する。

  • 高海千歌[こっちのボタンを......あれ?] (作戦切り替え軽減・スタミナ回復)

f:id:skty001:20211226213213j:plain

f:id:skty001:20211226213227j:plain

  • シールド獲得特技持ちのピュア属性Gd2人
  • ダメージ軽減特技持ちのピュア属性Gd3人
  • ピュア属性のボルテージ稼ぎ要員3人

アクセサリーに強い依存はないが、以下を強く推奨する。

  • スコア稼ぎのためのバングル(クリティカル値増加)
  • ダメージ軽減のためのネックレス(ダメージ軽減)
  • AC9のためのベルト(SP特技Vo増加)

攻略の方針

  1. スタミナ緑以上を厳守
  2. シールド優先
  3. 10万ダメージと割合50%ダメージは軽減
  4. 3万ダメージは素受け
  5. 達成時に大ダメージを受けるSPAC(AC1, AC5)は達成しない
1. スタミナ緑以上を厳守

 VoACの目標ボルテージがかなり厳しめなため、スタミナ黄以下だと達成が難しい。比較的安定するVoACを失敗すると、タダでさえカツカツなシールドが尽きる...

もちろん、Sランクを目指すなら尚更死守する必要がある。

2. シールド優先

 スタミナ回復不可デバフ中でも殴り続けなければいけないことと、シールドは張れること、また、スタミナ緑の維持が必須であることを考えると、スタミナを回復するよりもシールドを張るべきである。

3. 10万ダメージと割合50%ダメージは軽減

 全ダメージを素受けしてるとシールドが保たないため、10万ダメージと割合ダメージに関しては軽減が必要。特に冒頭の10万ダメージは即死の危険性もあるので、死ぬ気で軽減する。

4. 3万ダメージは素受け

 基本的に3万ダメージは素受けしてシールドに吸わせる(AC5だけは軽減が必要かも...)特に、AC3, AC7中の3万ダメージ×3は軽減のために作戦を切り替える暇すらないので、9万ダメージを受けきれるシールドを張っておかなければならない。

5. 達成時に大ダメージを受けるSPAC(AC1, AC5)は達成しない

 検証してないが、達成で受ける10万、15万よりも未達成で受けるペナルティのほうが小さい(はずの)ため。特に、AC1は13万ダメージとほぼ同タイミングで受けなくてはならないため、かなり軽減が厳しい。

ここで落とすVoは他で稼ぎたい。

編成

f:id:skty001:20211226205354j:plain

 筆者の編成は上記の通り。本攻略も上記の編成、もしくは上記に準ずる編成を前提とする。

 ※ひらめきスキルはほぼ埋めていない。急造ユニットなので...。

  • 青作戦:シールド獲得特技2人と高海千歌[こっちのボタンを......あれ?]

 シールド+回復作戦。シールド獲得特技に関しては、ピュア属性である程度のシールドを張れる子なら誰でも良い...が、↑の2人でかなりギリギリなので、最低ラインと考えるべき。

 千歌は作戦切り替え時1ノーツ44%軽減の個性を持つため、冒頭を抜けるのに活躍してくれる。加えて、スタミナ回復特技のため、多少のダメージは戻してくれる。割と今回の必須級キャラクターかもしれん。

  • 緑作戦:ピュア属性の軽減特技3人

 軽減作戦。ピュアURで軽減特技持ちは2人しかいないため、SRに出張してもらった。曲冒頭で軽減が必要であることと、青作戦に置いた千歌の作戦切り替え軽減個性を有効活用するため、曲開始時のデフォルト作戦である緑作戦を軽減作戦とした。

  • 赤作戦:Voが高いピュア属性3人

 メイン作戦。ここは人によるが、VoACを超えられる火力はほしい。↑の3人で3,4ノーツ残しで達成なので、やっぱりこっちも最低ラインと考えるべき。

攻略

本曲は大きく分けて4つのセクションに分けられる。

  1. 冒頭 (AC1+13万ダメージ)
  2. スタミナ80%維持AC → 大ダメージノーツを含むVoAC(AC2→AC3, AC6→AC7)
  3. AC5 (SPAC + 6万ダメージ)
  4. ラストスパート (AC8, AC9)
1. 冒頭 (AC1 + 13万ダメージ)

 曲が始まってから8ノーツの間に、10万ダメージ+3万ダメージとSPACが降り注ぐ。

 本攻略では、SPACの達成は不可と考え、13万ダメージとAC失敗ダメージを軽減する方針とした。具体的には、曲開始時から軽減作戦で特技発動をお祈りし、10万ダメージを軽減し、続く3万ダメージとAC失敗ダメージの直前で作戦を切り替え、軽減作戦と千歌の個性で軽減する。うまく噛み合えばスタミナ緑が維持できる...が、大体は半分から8割くらい吹き飛ぶので...祈れ♡

(3万ダメージとAC失敗ダメージが同ノーツ扱いなのは優しさ...ですかね...?)

2. スタミナ80%維持AC → 大ダメージノーツを含むVoAC(AC2→AC3, AC6→AC7)

 序盤と中盤にスタミナ80%維持ACと大ダメージノーツを含むVoACが続くセクションがある(14~106ノーツ、158~233ノーツ)。ここで重要なのが2点。

  • スタミナACはスタミナ80%未満から80%に戻す時間ではない。シールドを張る時間
  • VoAC中は軽減作戦に変える暇はない。

 VoACの目標ボルテージがかなり厳しく、常にメイン作戦で殴り続けなければいけないため、VoAC前には9万ダメージを捌けるシールドを用意する必要がある。そのため、直前のスタミナAC中にシールドを稼ぐ必要がある(=スタミナ回復要員を用意する余裕がない)ため、スタミナAC前にはスタミナを80%以上にしておきたい。(千歌のおかげで多少のカバーは可能)

スタミナAC終了時にスタミナゲージの7割くらいのシールドはほしい。

3. AC5 (SPAC + 6万ダメージ)

 これマジで分からん。

 AC5は達成時ダメージを避けるために失敗を前提とするので、AC4達成後に軽減作戦に切り替えて6万ダメージとAC失敗ダメージを軽減する。できれば続くスタミナAC→VoACのためにスタミナ80%以上で乗り切りたいが...運ゲーだな?

4. ラストスパート (AC8, AC9)

 殴りどころ。

 これまでのACと比べて殺意が薄いため、クリア目的ならシールド+回復作戦、軽減作戦で殴り続ければOK。

 スコア目的なら3回の3万ダメージでスタミナ緑~黄色を維持しつつ殴りきりたい。AC間の265~273ノーツでシールド稼いでおくと安心?ただし、AC開始即SP特技と終盤SP特技の2回のSP特技でSP65万を稼ぐ必要があるため、33万出せる準備をしておきましょう。

 

おわりに

 前回の「Love U my friends」チャレンジが簡単だった反動か、かなり強烈なチャレンジ譜面がきたなぁという印象でした。最初の8ノーツでFAILEDとか言われて思わず笑いましたね...。でも、最高難易度はこうあるべきだと思いました。

 多分、戦力が揃っていればもっとスコアを稼げて安定する方法もあるかとは思いますが、とりあえずこんな感じでSランクが目指せそうで安心です。

 これ見て初クリア、Sランククリアできたら、焼き肉かiTunesカード1万円分を下さい!

就職活動のおもひで

2回目の転職活動が始まったので、酒に酔いながら適当に新卒時と転職(1回目)のことを思い出して雑文を書いてみようかなって。

 

  • 新卒(2015)

 アホでした。

 「情報系学生だしコンピュータ系以外無理」という考えだけで就職活動を開始。

 求人票に「システムエンジニア」と記載されている求人を漁っていたが、当時はシステムエンジニアという職がなんなのか考えないまま、システムエンジニアプログラマという認識のもと活動していた。また、上流・下流という話も偉い・偉くないに置き換え、偉いほうがいいと上流、しかも最上流を志望していた。

 今思うと、あくまで技術者(コンピュータ屋さん)としてやっていきたいんだから、最上流に突っ込むのはどう考えても悪手だったなと思う。どう考えても向いてねぇし。当時、先輩がベンチャー企業に入社したと聞き、「大学院まできてもったいねぇ~学歴捨てる気かよ」という感想を抱いたことが、俺の当時の考えを如実に表してると思う。

 業界に関しても、当時の俺が知ってる会社(ネームバリュー特大の会社)に申し込んでいたため、業界は二の次だった。業界が二の次なので、当然組み込みエンジニアだのWebエンジニアだの、「システムエンジニア」という職種の小分類に関しても無関心だった。最悪、業界に無関心でも、せめて...せめて職種に関してよく考えていれば...と悔やむばかりである。

 そんな考えで面接に挑むため、志望動機なんかボロボロでした。「情報系出たのでこんぴゅーたやりたいです」なんて学生をだれが取るというのか。

 1社目の面接であった某N社の社会インフラ事業部とやらの面接には「社会インフラって何だろう」とかそういうレベルの思考で臨んでいた。志望動機聞かれた時の回答のチンプンカンプンさは、我ながら今でも忘れねぇ。

 また、某Y社の面接では志望動機に「福利厚生」「研修内容」を答えるなど、あまりにも...あまりにも自分が求められている答えを理解して無さすぎるだろ...という立ち振る舞いであった。

 その後もよく考えないまま片っ端から面接に臨み、最終的には1社から内定が出たが、正直そこの面接でもマトモな志望動機を話せた覚えがない。たまたま発した言葉が上手く引っかかったのだろう。自分から運ゲーにしているのである。

 んで、苦労して運良く引っかかった会社を半年もしないうちにメンタルをやって休職するのであった。一度部署異動を挟んでいるが、今思うと、上司が強烈なクラッシャーだった異動前はともかく、異動後はどうとでもなったやろ!!という気持ちが強い。

 

ちょっとは頭を使って将来を考えろ!いつまでも学生気分でいるんじゃねぇ!新卒カード捨てるなあああああああ!!!!!!!!!

今の俺が当時の俺に送る言葉である。

 

  • 転職(1回目)(2017)

 1年と2カ月ほど休職したあとの転職活動である。400連休ってやべぇ。

 失敗の記憶と休職中の動かない頭での思考から、(最上流付近の)システムエンジニアは向いてない、手を動かす技術者としてやっていきたいと考えた。なんだかんだ、プログラム書くの好きだし、そういう職種がいいよねって。これは今考えても概ね間違ってないと思う。

 という感じで、ゲーム・モバイル・Webのプログラマあたりを主に転職活動を始める。冷静に考えるとプログラマシステムエンジニアの小分類なので、なんかやっぱ考えがズレてるよな~と。

 1社目の面接でやらかしがあり、自分から「いろいろやってきたけど広く浅くで特別できることはない」と発するなど、採用面接の場にいることを理解していない振る舞いが目立つ。要は自分に自信がなく、休んでいたという引け目もあり、ひたすらに卑屈なのである。幸い、人並みの受け答えはできていたとは思うけど、あまりにも話す内容が悪かった。

 そんな1社目での強烈な失敗の後、新卒の時とは違い、以降の面接では現職での数少ない経験や(休職中に作った)プログラムについて話すように改め、なんとかぽろぽろと1次面接通過の知らせを受けるようになったころ、当然の事実に直面する。当時、休職してることを話さずに面接に臨んでいたが、働くこと・2年間働いてきた人間として扱われることに恐怖を抱き始めたのである。その恐怖に耐えられず、その時点で選考を受けていた企業をすべて辞退した。その中にはほんまに行きたいと思っていた企業も含まれており、未だに後悔に苛まれるのであった。

 その後、休職していたことをオープンに活動を再開したが、やはり書類で弾かれることが多くなった。面接に漕ぎつけても、休職という後ろめたさが邪魔をして、〇〇が出来ます!と話せなかったように思う。

 結局、ほぼ形だけの面接で内定を出す人売り系SES会社に内定を貰い、1度目の転職活動が終わった。組み込みエンジニア屋さんである。

 この記事書き始めたときは当時の俺をぶっ叩くつもりでいたけど、なんか当時の俺よく頑張ったな?という感情に包まれている。強いて言えば、同じ人売り系SESでもITに強い会社選ぶべきだったな?(入社したSESは機械・電子に強くITがくそ弱い)

 売られた先で前職と同じ苦しみに襲われるのは別のお話。

 

  • 転職(2回目)(2021)

 今。先日1社目の面接に臨み、無事祈られた状態。

 今回は現職の組み込みエンジニアからWebやモバイル等のエンジニアへのキャリアチェンジ()を目的として転職活動をしているが、やっぱり業界が変わることに強いプレッシャーがあり、組み込みでやってきたことは他の業界に移るうえで価値がないという考えがあった。1社目の面接の場でも、これまでやってきたことに対して自信なさげに卑屈に語ったことが致命傷だったように感じる。

 今の仕事、しんどいながらもクッソ真面目にやってきたし、周囲のサポートなしに組込屋さんとしてそれなりになったし、なんならプロジェクト回ってるの俺のおかげやし、正直、業界が変わってもやればできるでしょwとは思っているが、「今の仕事でこんなことやってきました!!!!!こんなことできます!!!!!領域変わるけど次の業界でも同じようにできます!!!!」的な意気込みが伝わらないと、こいつ逃げてきただけだな?やらせてもできないな?と思われて「スキル不足」の5文字で落とされるのである。

 書きながら思ったけど、この「スキル不足」は転職先の業界のスキル不足じゃなくて、転職前の業界でうまくやれてない→コンピュータ全般のスキル不足(だと思われた)を意味してるのでは?????そら今の領域できないのに別の領域できるわけないわ!目から鱗だわ。本当かは知らんけど。文章書くってすげー。

 曲者上長に言われた「(技術力の多寡はともかく)組み込みエンジニアとしてこんなことをやってきましたと自信を持って話してこい」というのは真理なんだと思う。

 そもそも書類通ってるんだから、組み込みから別の業界に行こうとしてることは分かった上で面接に呼んでるってことだよな?????どうせ相手は組み込みの深いも浅いも分かんねぇんだから、俺がやるべきは勢いよく「こんなことやってきました!!!」だよなぁ???????

 1回目の転職と違い、今回は実績があるのである。そこだけは自信を持っていこうと思う。

 

  • んで

 転職活動は自分のこれまでを振り返る活動とは言うが、その通りなんだと思う。同じ修士卒と比べて2年のディスアドバンテージがあるとはいえ、3年間組み込みエンジニアとして真剣にやってきたなら、それをしっかり自信を持って話すべきなんだろう。

 新卒・転職(1回目)・転職(2回目)全てで1社目でやらかしを演じているが、少なくとも転職(1回目)・転職(2回目)ではそのやらかしで地に足をつけるというか、現状が見えてくるって意味では必要なやらかしだったんだと思う。

 恐らく転職活動に限らず、物事は1発目で失敗してからが本番なんだなぁと朧げに思うのであった。

【Watch OS】AppleWatch向けDDRハイスピ計算機を作ってみた

  • 概要

Dance Dance Revolution(以下DDR)のハイスピード機能(以下HS)は、選曲前に「BPM×HS倍率=プレイヤーの適性スクロールスピード(以下SS)」をプレイヤー自身が計算し設定するシステムである。基本的には暗記もしくは暗算が必要となるが、キリの良いBPMならばともかく、疲労感の強い状況でキリの悪いBPMのHS倍率を計算するのは正直しんどいというのがプレイヤーとしての本音である。(236BPMのHS倍率に迷ったりする)

そこで、快適なHS調整を目的としたApple Watch向けHS計算機を作成する。

f:id:skty001:20200928163442p:plain

 

 

 

  • 開発環境

PC:MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)

OS:macOS Catalina 10.15.7

IDEXcode 12.0.1(12A7300)

言語:Swift

FW:SwiftUI

github.com

 

  • 本アプリを作る上で考えたこと

  1. Apple Watchの小さい画面をポチポチするようなモノにしたくないので、Digital Crownをメインとした操作とする。
  2. 適性SSピッタリのHS倍率にならない場合を考慮し、目標SS以下HS倍率・目標SS以上HS倍率の2通りのHS倍率を表示する。 

 

  • 画面レイアウト

下記画像は、本アプリをシミュレータ(Apple Watch Series 6 44mm)で動かしたもののスクリーンショットである。

f:id:skty001:20200928163446p:plain f:id:skty001:20200928164214p:plain f:id:skty001:20200928163647p:plain

  1. 楽曲BPM・目標SSをタップすると文字色が青になり、DigitalCrownで設定ができるようになる。目標SSはプレイヤーごとに殆ど決まった値になるためタッチ操作を許すこととした。
  2. 楽曲BPMの最大値はTohoku EVOLVEDのラストを想定して1040とし、それに伴い目標SSの最大値は1040×8=8320とした。
  3. 画面下部(青い線の下)には、楽曲BPMと目標SSから算出したHS倍率と実際の計算結果を表示する。上のHS倍率は目標SS以下で最も大きいHS倍率、下のHS倍率は目標SS以上で最も小さいHS倍率となる。

 

  • 今後の目標

  1. 段位認定やコースでの使用を想定して、4曲分のHS倍率を保存して表示する機能を実装する。HS倍率計算画面とは別に保存HS倍率表示画面を専用に作る感じになると思う。
  2. 心拍数を表示する機能を実装する。現在の心拍数に加え、1曲2分として3分間の最大心拍数、平均心拍数を表示する。できればHS倍率計算画面・保存HS倍率表示画面の下部に表示したい。
  3. DigitalCrownでの値設定において細かい操作が難しい問題があるため、操作性の向上を考える。オプションとしてテンキーでの入力に対応するかもしれない。できれば〜できれば〜HS倍率計算画面を表示したままで音声入力できたら楽だよね〜…雑音が多いしエラー処理も必要だしで結構大変そう。
  4. ストアに公開したい。

 

  • 技術的なこと

  •  Digital Crownの状態取得について

以下はDigital Cronwの状態を取得するメソッド digitalCrownRotation()のリファレンス。

developer.apple.com


 

 以下は、digitalCrownRotation()の定義。

func digitalCrownRotation<V>(_ bindingBinding<V>, from minValue: V, through maxValue: V, by stride: V.Stride? = nil, sensitivityDigitalCrownRotationalSensitivity = .high, isContinuousBool = false, isHapticFeedbackEnabledBool = true) -> some View where V : BinaryFloatingPoint, V.Stride : BinaryFloatingPoint

このなかで今回使用及び検証したものについて説明する。(採用は太文字)

 

  • _ binding: Binding<V>

DigitalCrownが回転したとき値の増減(更新)を行う変数を指定。

  • from minValue: V

値が取り得る最小値を指定。

  • through maxValue: V

値が取り得る最大値を指定

  • by stride: V.Stride? = nil

値の刻み幅を指定。1と指定すれば1刻みで値が増減する。

  • sensitivity: DigitalCrownRotationalSensitivity = .high

minValueとmaxValueの間を移動する速さ(DigitalCrownの感度)。.high, .medium, .lowから指定。指定なしの場合true。

  • isContinuous: Bool = false

minValueとmaxValueの間を繋げるか否か。trueの場合、minValueを下回った時にmaxValueになり、maxValueを上回った時にminValueになる。指定なしの場合false。

  • isHapticFeedbackEnabled: Bool = true) -> some View where V : BinaryFloatingPoint

DigitalCrownを回しているとき触覚フィードバック(ブルブル震える)を発生するか否か。指定なしの場合true。

 

 本アプリから楽曲BPMを表示するコードを抜粋し、シンプルにしたもの。

■sample.swift

struct ContentView: View {
    @State var inputBPM : Double = 440
    // 中略
    var body: some View {
    // 中略
        Text(String(Int(inputBPM))
.digitalCrownRotation($inputBPM, from:0, through:1040, by:1) //中略 } }

本テキストボックスは楽曲BPMを格納している変数inputBPMの値を表示する。

inputBPMの値をDigitalCrownの回転で制御するため、inputBPM自身と楽曲BPMが必要とする値範囲をdigitalCrownRotationの引数で指定している。

  • 値を増減させたいのはinputBPMなので_ binding:には$inputBPMを指定。このとき$をつけて参照渡しにしなければならない。
  • inputBPMの取り得る最小値・最大値として、from minValue:には0を、through maxValue:には1040を指定。
  • inputBPMは小数を取らず1刻みの整数となるためby stride:には1を指定。

これで、DigitalCrownを回したときinputBPMの値が更新される。inputBPMには@Stateがついているため、更新に伴いContentViewが再描画され、更新後の値が表示される。

実際はinputBPMの値をHS倍率を計算する関数に渡し、それらもまとめて計算しているが、inputBPMが更新され再描画されることには変わりない。

  • ただの感想

ここからは業務でC言語を主に用いているプログラマが、Swift/SwiftUI等を20時間ほど触ってみて感じたことの覚書。

普段、それっぽいワードを使わないので変な使い方してたら...すみません

  1. 【SwiftUI】UIのプレビュー機能(デザインキャンバスのダイナミックリプレースメント?)がすごく便利。UIを弄ると即座にビルドされプレビューが更新されるため、シミュレータを動かさずともUIが確認できる。
  2. 【SwiftUI】View作成はコードベース。Windowsフォームアプリ等で見られる、フォームにボタンやテキストボックス等を設置して、それぞれの動作を設定していくものではない。が、それはコードかGUIかの違いであり、UIレイアウトの知識は必要。
  3. 【SwiftUI】SwiftUIのViewは@Stateをつけた変数の値が変化するたびに更新される。基本的には外部のセンサやButtonタッチ時のaction等で値が変化することを想定している?多分ViewとControllerの分離というやつで、ViewのBody内で値の代入等の計算はできないっぽいので、@Stateをつけた変数を自作の関数に渡し、戻り値を画面表示等に用いるようにした。
  4. 【Swift】他言語の貯金があるのでSwift自体にはそれほど苦労しなかった。とはいえちゃんと勉強する必要があるな〜とは常々。
  5. XcodeXcodeのバージョンとビルドできるiOS/WatchOS/etc...のバージョンが決まっているのが驚きだった。AndroidなんかだとSDKだけ引っ張ってくれば良いのかな?よく考えてみれば当たり前だけども...。Xcodeの過去バージョンはAppleが配布している(https://developer.apple.com/download/more/)
  6. 【AppID】アプリと紐づくID。発行上限は一週間で10個。
  7. 【実機デバッグ】AppleWatch向けアプリを実機で動かすには、AppleWatchとペアリングしたiPhoneMacに接続する必要がある。iPhoneからAppleWatchにぶん投げる感じ。Apple製品が3つ必要とはたまげたなぁ。

 

  • 感想

約5年ぶりのスマートデバイス向けアプリの開発だった。普段日常的に触れているデバイスで自分の作ったものが動作するのは嬉しみが溢れる。基本的に全部自分で用意する組み込み系と違い、OS側で用意されているものを如何に使うか?みたいなところが焦点になってるのかな〜?なんて感じた。

普段使っている言語etcとは全く別のものを使って何かを作ってみるのは、普段と別の脳を使っている感じでやりがいがあるな!でも別に普段の経験が無かというとそうでもないのが面白い。

今後の目標に挙げたものを達成していきまぁす!ストア公開までやり切って、他のDDRプレイヤーが使ってたら嬉しい。若干ニッチだけど!

CNNを利用したアニメキャラクターのフェイストラッキング

はじめに

 CNN(Convolutional Neural Network)を利用して、ラブライブ!のμ's9人の顔を学習し、未知のラブライブ!イラストに写っているキャラクターの分類を行った。

処理の流れ

  1. インターネットからラブライブ!関係の画像を収集する。
  2. OpenCVを利用して、収集した画像からキャラクターの顔を抽出する。
  3. 抽出した顔画像を手作業で10クラス(キャラクター9人+背景)に分類する。
  4. CNN(AlexNet)で学習する。
  5. 学習したモデルを利用し、動画(ダンスシーン)の各フレームに対し、顔抽出と分類を行う。

開発環境

OS : macOS High Sierra 
言語 : Python 3.6.3 
ライブラリ : TensorFlow, Keras, Numpy等データ処理ライブラリ 
API : 

LoveLive! SchoolIdol API 

github.com

 

Twitter API:

Twitter Developer Platform — Twitter Developers


GitHub:
FaceDetector

github.com

画像収集

 学習用画像として、ラブライブ!関係の画像を収集する。

 まず、「LoveLive! SchoolIdolAPI」を利用して、各キャラクターの全カード画像・楽曲ジャケット画像を取得した。カード画像に関しては、背景が学習の妨げになると考え、キャラクター以外の領域が透過されているものを利用した。計1579枚。

f:id:skty001:20180131132730p:plain

 

 次に、Twitter上でラブライブ!のアニメやライブシーンのgif画像をツイートしているアカウント(ラブライブ!gif画像 (@llimgfunbot) | Twitter)を利用し、gif画像を1700枚取得した。取得したgif画像をバラして学習に利用するが、全フレームを対象とすると、

  • 学習用画像数の増加に伴い、学習時間が膨れ上がる。
  • 隣接したフレームでは画像の変化が少ない。

といった理由から、gif画像の10フレームにつき1フレームを抽出した。計17861枚。

f:id:skty001:20180131132704p:plain

 

 

顔抽出

 前節で収集した19440枚の画像から、OpenCVを利用して顔抽出を行う。

 ここで得られた計12011枚の顔の画像を学習用顔画像とする。

f:id:skty001:20180131133747p:plain

 

 顔抽出においては下記の記事を参考にした。

qiita.com

 上記の記事で紹介されている方法で顔抽出を行う場合、アニメキャラ用の顔抽出用カスケード分類器「lbpcascade_animeface.xml」が必要になるので注意。下記のサイトでダウンロードできる。

github.com

 

画像分類

 前節で得た学習用顔画像12011枚をキャラクターごとに分類する。今回はμ'sのキャラクターを分類するため9クラスとなる。また、OpenCVの顔検出が上手くいかず、キャラクターの顔でない部分が抽出されることがあるため、誤検出を検出するためのクラスを1つ用意した。このクラスには主に背景が分類される。よって、今回の分類対象は、キャラクター+背景の10クラスとなる。

 この手順ばかりは手作業でやるしかないので、覚悟すること。とてもしんどかった。

f:id:skty001:20180131134622p:plain

 

画像の水増し

 前記事で分類精度を上げるためには画像数が重要であり、画像数を手軽に増やす方法として、学習用画像を回転・反転・ノイズ付加する方法があると述べた。今回も画像数の水増しを行う。具体的には、学習用顔画像を時計回り、反時計回りに15°回転させる。これにより、学習用顔画像は3倍の36033枚になった。

 

モデル

 当初、CNNのモデルを自分で構築する気でいたが、モデルを少し考えたところで、自分で苦労して考えたモデルよりも、既に実績があり一定の評価を受けているモデルを利用したほうが賢いことに気がついた。

 そこで今回は、画像処理コンテストIISVRC(2012)で優勝した実績のあるAlexNetを利用することにした。詳細は下記参照。

[ディープラーニング] AlexNet – Tech Memo

 実装したモデルは下図のようになった。そこそこ複雑なネットワークに見えるが、要は単純な層を繋げているだけなので、実装はそこまで難しくなかった。(内容を理解しているとは言っていない)

f:id:skty001:20180131135437p:plain

 なお、入力はH244 W244 RGB 3チャネルなので、学習用顔画像をこのサイズにリサイズする必要がある。

 

学習

 構築したモデルに学習用顔学習を学習させる。

  • 学習用画像 : 21619枚, 評価用画像 : 14113枚(CV 0.6)
  • エポック数 : 40
  • バッチサイズ : 32

 筆者の環境では学習に丸一日かかった💢。GPUが乗っている環境であれば、TensorFlowのGPU対応版を使うことでもっと早く学習できると思う。

 筆者の怠慢で学習の過程の精度等をメモするのを忘れてしまった。体感では(記憶の中では)エポック0-エポック15間にAcc0.8まで上がり、そこからエポック40まで少しずつ上昇し、最終的にはAcc0.9ほどになった。何の役にも立たない体感だァ…次はちゃんとlog取ります…。

 

分類

 学習したモデルに、未知の顔画像を突っ込むと9+1クラスのいずれかに分類してくれる。今回は2種類の動画の各フレームに対し、顔認識・分類を行った。

  • 【フェイストラッキング】コールアンドレスポンス
     - 動画の各フレームに同時に映るキャラクターが1人ずつ
     - キャラクターの動作が少ない
     - キャラクターが正面を向いている
    以上の理由から、正常にキャラクターを分類できているかの確認が容易。

  • 【フェイストラッキングユメノトビラ
     コールアンドレスポンスと比較して、
     - 各フレームに複数のキャラクターが映っている
     - キャラクターの動作が大きい
     - カメラワークが激しく、正面だけでなく横顔や上を向いているシーンが多い
    以上の理由から、本分類器が実用に足るかの確認ができる。

 

結果 

 四角の枠の左上の文字が、分類したキャラクターの名前。その隣の数字が分類の尤度。Eli[0.99834]の場合、99.8%の確率で絵里であると判別したという意味。


【フェイストラッキング】コールアンドレスポンス

 概ね上手くいっているが、顔をアップで映しており、髪型が画面外に移動したフレームや、キャラクターが動くことによって前髪が大きく動いている(崩れている)ときに誤判別が発生している。これより、キャラクターの判別方法の一つに、前髪を含む髪型があることが予想できる。

 


【フェイストラッキング】ユメノトビラ

 かなりカメラワークやライティングの変化が激しく、キャラクターの輪郭などのブレが大きいため、顔の検出が上手くいっていないフレームが目立つ。しかし、検出ができているフレームに関しては、概ねキャラクターの判別が上手くいっているため、本プログラムではなく、OpenCVの顔検出機能の精度に問題があることがわかる。
 解決策としては、
  ・横顔や輪郭のブレに強いOpenCVの顔抽出用カスケード分類器を自作する。(OpenCVで解決する場合)
  ・顔周辺の画像特徴量を調べ、それらを抽出できるプログラムを自作する。(OpenCVを使わない場合)

 訓練画像の数や質を向上する等の力技では解決できず、本格的な画像処理や数学処理を使わざるを得ないため、割りと茨の道である。改善する場合は本腰入れて学習が必要。


まとめ
 
・訓練画像データの選別が一番の鬼門
 ・CPUでディープラーニングをするのは非効率。可能であればGPU版TensorFlowを使い、学習の時間を短縮すべき。
 ・フェイストラッキングに関しては、OpenCVの顔抽出機能の性能がネックになる。