4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

譜面からDance Dance Revolutionの難易度を推し量るモデルをDNNで作った

Last updated at Posted at 2022-10-29

Dance Dance Revolutionについて

運動したほうがいいと周囲に言われたため, Dance Dance Revolution(以下DDR)に取り組んでいる.
DDRというのはリズムゲームの一種で, ディスプレイ上の矢印に併せて筐体のパネルを踏むゲームである.
DDRにはstepmaniaというエミュレーターがあり, このstepmaniaの譜面データから, 適切な難易度を類推するモデルを畳み込みニューラルネットワークで作成した.
image.png

学習データについて

stepmaniaの譜面を公開しているサイトから, テキトーに「.sm」をダウンロードし学習データとした.
今回は, 約5000譜面を学習データとし, 加えて左右の上下の入れ替えることでデータの水増しを行った(左右や上下を入れ替えても難易度がそこまで変わらないと仮定).

譜面を(時間)×(状態)の二次元配列に変換し, それをモデルの特徴量とした.
目的変数は,その譜面の難易度(19段階)である.

時間軸について

一部譜面には12分や32分などの矢印があるものの, 多く(85%ぐらい)の譜面は4分8分16分のみで構成されている.
そのため, 1小節を16等分する形で, 時間軸を表現した.
12分や32分などの特殊な矢印は, それと位置が近い16分の箇所に書き換えを行った.

状態軸(矢印の種類/BPM)について

DDRの矢印を(上下左右)×(矢印の種類:計4種)の計16通りに分類した.

  • 普通の矢印
  • フリーズアロー(長押し)の開始地点
  • フリーズアロー(長押し)の終了地点
  • ショックアロー(踏まない)

この16次元は, 該当する矢印があれば1なければ0のベクトルである.

FA SA

加えてDDRではなぜかBPMが変化する曲が多い.
そのため, BPMの状態も入れ込む必要がある.
よって, 状態16種類+BPM一種類の計17次元のベクトルを入力データとした.

学習データのまとめ

image.png
上記の二次元配列を最終的な特徴量とした.

学習モデルについて

今回は時系列解析等で使われる, 一次元の畳み込みニューラルネットで難易度類推器を作成した.
通常のそれとは違い, GlobalMaxPoolingとGlobalAveragePoolingを並列して行っている.
これは, 「最大の難所」と「全体の平均難度」の2つを抽出することを目的としており, 実際に精度が向上した.
image.png

コードの概要は以下になる.

input_x = keras.layers.Input(shape=(2704,17),name='in_X')
input_l = keras.layers.Input(shape=(1,),name='in_l')

u1= keras.layers.Conv1D(128,32, activation='relu')(input_x)
u1 = keras.layers.MaxPooling1D(5)(u1)

u_gmp = keras.layers.Conv1D(128,16, activation='relu')(u1)
u_gmp = keras.layers.GlobalMaxPooling1D()(u_gmp)
u_gap = keras.layers.Conv1D(128,16, activation='relu')(u1)
u_gap = keras.layers.GlobalAveragePooling1D()(u_gap)
u_gap = keras.layers.Multiply()([u_gap,input_l])

u2 = keras.layers.Concatenate()([u_gmp,u_gap])
u2 = keras.layers.Dense(64, activation='relu')(u2)
u2 = keras.layers.Dense(16, activation='relu')(u2)

out = keras.layers.Dense(1, activation='sigmoid')(u2)
model = keras.Model(inputs=[input_x,input_l], outputs=out)

入力データは可変長配列のため0埋めしてサイズを揃えている.
よって, そのままだと適切に平均が計算できない.
input_lはその補正項である.
$$input_l=\frac{最大の譜面の長さ}{その譜面の長さ}$$

結果

validationのRMSEが0.6程度となった.
そのため, 適切な難易度の目安になること程度なら期待できる

output.png

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?