2018年7月7日土曜日

フリゲ紹介: ビギニングクエスト ~ベクセリアワールド物語~

ビギニングクエスト ~ベクセリアワールド物語~は、ドラクエ風味のファミコン風RPG。 最初プレイした時は序盤が辛めだったのですが、最近の更新で良いゲームバランスになっていたため、一気にプレイしてみました。 私はDLしてプレイしましたが、RPGアツマールでもプレイできる。 さらに2018年の自作ゲームフェスで大賞を受賞していたみたい。



主人公の性別は選択可能ですが、ひとまず可愛いので女の子にしてみました。 いかにもドラクエな感じでスタートします。システムもドラクエらしくタンスやツボ、本棚などを調べる事ができる。



戦闘やマップはファミコン版のドラクエ3が近いかな。 ただし鬼畜さはあまりなく、サクサクプレイできるくらいの難易度です。



ドラクエやFFライクなレトロRPGは、有名所ではTDQやDRAGON FANTASYなどの名作があります。 他にもFF 3.5Wish Dragon僕は勇者じゃないよなど、良い作品がたくさんあって、私はどれも好きですね。 同作者様が作成したクラシックレジェンド ~悠久のノスタルジア~もお気に入りの作品。
こういったレトロRPGもゲームバランスは現代風に調整されているものが多いので、とっつきにくい作品は最近はあまりない気がします。 ビギニングクエスト ~ベクセリアワールド物語~もゲームバランスが改善されて、気軽に楽しみやすい作品になった気がします。

ファミコン時代の感触に浸りつつ手軽に楽しむには良い作品。

Deep Learningの回帰における出力分布の重要性

以前から気になっていた事なのですが、Deep Learningはデータセットの分布がかなり重要です。

主に正規分布を仮定する事になる訳ですが、その重要性について書いてみようと思います。 sin波の予測を例にすると非常にわかりやすいので、まずは簡単なコードを以下に載せます。

上記のコードは過去のinputs = 400の動きを元にplen = 400後の数値を予測します。 これを実行すると多少のノイズがあるsin波でも正負の判定なら92%くらい、誤差なら0.04くらいの精度で予測できる事が確認できると思います。 さすがはDeep Learning…で話が終われば良いのですが、本当にそうでしょうか。 例えば以下のコードのようにplenをランダムにするとどうなるでしょう。
def generate_dataset(dataframe, plen, inputs):
    x, y = [], []
    for i in range(len(dataframe)-inputs*2):
        tmp_x = dataframe[["sine_wave"]].iloc[i:i+inputs].values.flatten()
        tmp_x = np.append(tmp_x, 1.0/plen)  # +1特徴
        # tmp_x = np.append(tmp_x, np_utils.to_categorical(plen, inputs))  # +n特徴
        x.append(tmp_x)
        y.append(dataframe[["sine_wave"]].iloc[i+inputs+plen].values.flatten())
    return np.array(x), np.array(y)

def load_data(data, test_size, inputs):
    n = int(round(len(data) * (1 - test_size)))
    x_train, y_train = generate_dataset(data.iloc[0:n], random.randrange(inputs), inputs)
    x_test, y_test = generate_dataset(data.iloc[n:], random.randrange(inputs), inputs)
    return (x_train, y_train), (x_test, y_test)
plenの特徴量が何もないと悲惨な事になるのは明白なので、上記では2つの方法で特徴量として追加しています。 さて上記のコードはどのくらいの精度が出るでしょう。 結論だけ言ってしまえば10%〜80%くらいの不安定な精度になるはずです。 この程度の変更でもDeep Learningは簡単に学習できなくなってしまうのです。


「plenを特徴で追加したのになんで?」と思う方は居るかも知れません。 これが今回述べたかった分布の重要性。 plenと過去の数値から出力される1つの出力は、plenに対して正規分布にまったく従わない事が何となくわかるはずです。 一番最初のコードはplenが固定されていたため予測値が正規分布に従い、うまく予測できていただけなのでした。

ではどうすれば良いかと言うと、以下のコードのようにplenごとの出力を見ます。 この場合、各出力の誤差が正規分布に従う事が予想できるので、うまく学習する事ができます。 ちなみに+1特徴でも+n特徴でもさほど性能差は出ません。 とにかく出力を正規分布に従わせる事が重要なのです。
def generate_dataset(dataframe, plen, inputs=400):
    x, y = [], []
    for i in range(len(dataframe)-inputs*2):
        tmp_x = dataframe[["sine_wave"]].iloc[i:i+inputs].values.flatten()
        tmp_x = np.append(tmp_x, 1.0/plen)
        # tmp_x = np.append(tmp_x, np_utils.to_categorical(plen, inputs))
        x.append(tmp_x)
        y.append(dataframe[["sine_wave"]].iloc[i+inputs:i+inputs*2].values.flatten())  # 変更
    return np.array(x), np.array(y)

model.add(Dense(400))  # 1出力から400出力へ変更
上記のコードに変更すれば、任意のplenにおいても92%くらいの精度で予測できる事が確認できるはずです。 Deep Learningが神経を使う予測器である事、そして精度が出ない時の傾向と対策がよくわかる例だと思ったのでメモしてみました。

フリゲ紹介: Project Zero

Project Zeroは狂暴化した生体兵器に支配された研究所を調査するために派遣された傭兵主人公の物語。探索RPG。



黒ずくめの全身武装している彼が本作の主人公。華はないけどリアル感が出ていて私は好き。 近未来的な装備をしており、攻撃の主体は銃器です。



マップも臨場感があって良い。ヤバめな雰囲気がよく伝わってくる (左下)。 戦闘はサイドビューのアクティブタイム方式ですが、敵へのダメージの与え方や狙う部位によってかなり難易度が変わってくるので面白い (右下)。 強敵相手には戦い方をうまく選ばないと厳しい局面もあります。



こういったバイオハザード的なゲームは割と鬼畜な難易度である事が多いのですが、Project Zeroは主人公が強キャラなおかげか戦闘バランスは緩め。 かなり簡単なほうだと思います。 ただそのおかげもあってかサクサクとプレイできるので熱中でき、私は一気にクリアしてしまいました。

サクサクだけど熱中できるゲームをプレイしたい方にはかなりおすすめできる作品。

フリゲ紹介: さまよえる不死者たちの城

さまよえる不死者たちの城は、古城を舞台としてスケルトンなどのアンデッドたちと戦いながら、 かつて城で起こった秘密を明らかにして行くガンアクション探索RPG。



本作では貴族から依頼を受け古城を探索する事になった主人公であるケネス (左) が、探索の過程で様々な人と出会いながら古城で起こった出来事を明らかにしていきます。



戦闘はSHIFTキーで射撃モードに入ってZキーで撃つ、Cキーでリロードというシンプルな仕組みになっています。 ただし射撃モードに入ると方向転換できないので多少慣れが必要。



ゲーム難易度は全体的に易しめでサクサクプレイできます。 私はノーマルモードでクリアしましたが、ハードモードでもいけたかも知れない。 丁寧に作られた作品で、非常に印象の良い作品でした。

Kerasでsin波を予測してみる

sin波の予測はサンプルとしてかなり使いやすいのでサンプルコードを置いておきます。 LSTMなどで作っているサンプルが多かったのですが、シンプルな全結合で書いてみました。 様々な検証がしやすいように書いたつもり。

基本的なコードはこことかここを参考にしている。 次稿で回帰の特性を確認する例として取り上げたかったので、Pandasの最新の仕様に合わせたり、説明がしやすくなるように微調整しています。
import keras
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.layers.normalization import BatchNormalization
from keras.callbacks import EarlyStopping
import math, random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

def generate_data(period):
    random_factor = 0.5
    length = 500
    data = pd.DataFrame(np.arange(period * length + 1), columns=["t"])
    data["sine_wave"] = data.t.apply(lambda x: math.sin(
        x * (2 * math.pi / period)+ random.uniform(-1.0, +1.0) * random_factor))
    return data

def generate_dataset(dataframe, plen, inputs):
    x, y = [], []
    for i in range(len(dataframe)-inputs*2):
        x.append(dataframe[["sine_wave"]].iloc[i:i+inputs].values.flatten())
        y.append(dataframe[["sine_wave"]].iloc[i+inputs+plen].values.flatten())
    return np.array(x), np.array(y)

def load_data(data, test_size, inputs):
    n = int(round(len(data) * (1 - test_size)))
    x_train, y_train = generate_dataset(data.iloc[0:n], inputs, inputs)
    x_test, y_test = generate_dataset(data.iloc[n:], inputs, inputs)
    return (x_train, y_train), (x_test, y_test)

period = 100
data = generate_data(period)
# data[["sine_wave"]].head(period * 2).plot()
# plt.show()

inputs = 400
hiddens = 200
(x_train, y_train), (x_test, y_test) = load_data(data, 0.1, inputs)

model = Sequential()
model.add(Dense(hiddens, input_shape=(len(x_train[0]),)))
model.add(Activation('relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
for i in range(1, 4):
    model.add(Dense(hiddens))
    model.add(Activation('relu'))
    model.add(BatchNormalization())
    model.add(Dropout(0.5))
model.add(Dense(1))
model.compile(loss="mean_squared_error", optimizer="adam")

early_stopping = EarlyStopping(patience=0, verbose=1)
model.fit(x_train, y_train, batch_size=50, epochs=10, validation_data=(x_test, y_test), callbacks=[early_stopping])
score = model.evaluate(x_test, y_test)
print('Test score:', score)

predicts = model.predict(x_test)
positive = np.sum((predicts > 0) == (y_test > 0))
print('Test Accuracy:', positive / predicts.size)
上記のコードは過去のinputs = 400の動きを元にplen = 400後の数値を予測します。 これを実行すると多少のノイズがあるsin波でも正負の判定なら92%くらい、誤差なら0.04くらいの精度で予測できる事が確認できると思います。

薬価と物価

2018年4月からの薬価改定により、新薬開発に影響が出るという記事が散見されます。 実際どのような影響が出るのかと思って軽く調べてみました。ちなみに私は門外漢の一消費者です。

まず改定後の薬価については、まとめるよりは厚生労働省の資料をそのまま見たほうが正確なのでそちらも置いておきます (原文)。 細かいところを省いてざっくり言えば、
  • 改定に伴い薬価は医療機関等での販売価格の加重平均値(税抜の市場実勢価格)×(1+消費税率)+調整幅を基準値とする
  • 先発薬品は販売から5-10年目にジェネリック医薬品が存在への置き換え率を元に1.5%〜2%の割引率を適用する
  • 後発品への置き換えが進んでいる先発薬品は販売10年-13年目にジェネリック医薬品の薬価の2.5倍〜1倍へ段階的に引き下げる
  • 後発品への置き換えが進んでいない先発薬品は販売10年-15年目はジェネリック医薬品の薬価の2.5倍〜1.5倍へ段階的に引き下げる

販売額が大きい銘柄は基礎的医薬品や、不採算品などいくつかの条件で基準値が加算される薬品もあるようですが、 だいたいは上記のようになるようです。 おそらく大きく変化したのは新薬が市場実勢価格によって毎年6%価格引き下げされる点が、 5年間の価格保証と価格引き下げ率の減少率が最大でも2%になった事でしょう。 これは新薬を開発するのに大きな変化と言えるとは思います。


薬価改定を見た時、門外漢の私はこれで米国の薬価基準に近付いたのかと思ったのですが、 この資料を見る限り実際には全然違いました (p10)。

私が気になったのは米国に新薬の薬価は自由に変更可能(物価上昇に伴い上昇するのが一般的)という部分です。 米国は毎年2-3%のインフレをしており、そして通貨価値も上昇しているため、 医薬品が輸入超過の日本は毎年上昇する薬価を輸入している事になります。 1990年を基準にすると米国は2.03倍に物価が上昇しており、日本は1.15倍の上昇率になっています。 これに対しドル円はどこを基準にするか難しいですが、110円〜140円である事から物価差以上の変化はありません。 さらに米国はジェネリック医薬品も自由価格で売買されるため、製薬会社が自由に薬価を吊り上げている問題が知られています。 つまり2倍近い物価上昇率や米国市場の薬価の影響をモロに受けているという事になると思います。

こういった状況を見ていると、薬価が改定されたから薬品企業に大きな問題があるという訳ではないと思います。 どちらかと言えば以前から創薬にきちんと力を入れていた会社はむしろ競争力が強くなりそうだし、 またより重要と思われるのは海外での医薬品の戦略的取り扱いや、承認期間の差などではないかと思いました。 日本の承認期間が長い事は知られていて、当然長ければ長いだけ競争力は失われる事になります。 条件付き早期承認制度の導入など改善はなされているようですが、この点は大きな差がありそうです。 こういった枠組みが変化すれば、医薬品の価格は大きく下がるのではないかと感じますし、 日本の医薬品業界にも良い意味での変化があるように感じます。

ふと気になって調べていただけなのですが、思いのほか面白い事が色々とわかりました。

フリゲ紹介: 箱庭セレナータ体験版

箱庭セレナータ 体験版は、5人のキャラクターを自由に合流させたり解散したりして、ダンジョンの謎を解いていくザッピングRPGの体験版です。

体験版となっていますが、体験版の範囲で最後までプレイする事が可能で、これだけでも十分楽しめる作品と思いました。 キャラクターはそれぞれ異なる特殊能力を持っているため、その能力を活かしながらダンジョンの攻略を目指します。



例えば本作の主人公のアリス (左) は鍵を開ける能力を持っています。他にもフックショットを撃てるキャラなども。 戦闘 (右) はボス以外ではまず負ける事がない難易度です。ボスにもそうそう負けないと思う。 戦闘よりは終盤に近付くにつれて難しくなってくるパズル要素を解くのが面白かったです。



こういったパズル系のダンジョン攻略も好きですね。 似たような作品としてはグリムボルトやグリムボルトDeepが私は好きでしたが、そのような難易度の高いゲームと異なり、頑張ればまずクリアできるけどパズル要素が強いという作品も、やはり面白いですね。

完成版がどのような作品になるのか楽しみです。 最近は箱庭セレナータ ひとあそびver.という新たな体験版も出ていました。