強化学習: ノート3

読んでいる本(出典): Amazon.co.jp: 強化学習: Richard S.Sutton, Andrew G.Barto, 三上 貞芳, 皆川 雅章: 本

前回:ノート2 / 次回:ノート4
目次:強化学習

今日読んだページ: 32~36ページ
以下、自分の解釈・感想。

  • 前回のあらすじ:
    • やってみなければ行動の価値はわからないのでやってみないといけない(探査)。
    • 「何に学ぶのか」という観点で強化学習を説明すると、自分の行動の結果に学ぶ。
    • 「何がミソなのか」という観点で強化学習を説明すると、探査と知識活用のバランスをとるのがミソ。
  • 答えのない学習(行動に依存): 「この行動だと前の行動より結果が悪かったから、前の行動を優先しよう」
  • 答えがある学習(行動と独立): 「この行動だと正しい結果とこんな風にずれるから、修正しよう」
  • ここで、遺伝的アルゴリズム(9ページ)は何だったんだっけ、とふりかえると、あれは答えはないが、学習でもない。とにかくよい行動を探す、というもの。ある程度よい探し方を示すのがGA。じゃあ学習って何 = 「こうした方がよいのでは?」を反映すること、と思う。特に試行錯誤学習は、「こうした方がよい = よさそうなことをもっとやり、よくなさそうなことはやらないようにした方がよい」。これは教師あり学習ではない(併用はできる)。
  • 「しかし決定論的なタスクでさえ、仮定をいくつか緩めた場合には、探査を行うことが有利に働く(32ページ)」
    • スロットマシンの報酬の分散が0だとしても、報酬の水準が時々刻々と変わっていくなら探査しなければならないねというのはそうだけど、それ外からみて分散0(決定論的)というんだろうか的な。
  • ランダムな探査よりよさそうな(必ずよいとは言っていない)探査 = 現時点でより価値が見込める行動を選択しやすいように重みを付ける。
  • 「ある行動が正しいかどうかという属性は相対的なものであり(34ページ)」: 社会でもそう。
  • 教師あり学習で探索するのはシステムのパラメータだが、強化学習で探索するのは行動である(34ページ)。



35~38ページの2値バンディットタスクを実装する。

  • 左図が36ページの図2.2のA、右図が図2.2のBに相当する。
  • 赤色が教師あり学習で、行動の最適度が60%程度にしかならない。
  • 青色がεグリーディ(ε= 0.1)。

f:id:cookie-box:20160206193009p:plain:w660

教科書より若干ギザギザしているのは、試行が1000回のため(教科書は2000回)。

  • 教師あり学習(赤色)が左右の絵で異なる理由(正解があることを前提にしているのでこういうことになる):
    • 左図ではどちらのスロットマシンも成功率が低いので「別のスロットマシンを選ぶのが正解なのでは?」という判断をし続けている(前回選んだのと違うスロットマシンを選び続けている)。
    • 右図ではどちらのスロットマシンも成功率が高いので「こちらのスロットマシンを選ぶのが正解なのでは?」という判断をし続けている(1つのタスク内ではずっと同じスロットマシンを選び続けている)。

L_{R-P} と L_{R-I} も絵に追加したい。

n.draw <- 500 # 1回のタスクでアームを何回引くか

# ベクトルのうち一番大きい値のラベルを返す補助関数
# ※ 同率1位があるときはランダムな選択にする
get.best.label <- function(data.hist) {
  label <- names(which.max(data.hist))
  data.hist <- data.hist[data.hist == data.hist[[label]]]
  labels <- names(data.hist)
  return(labels[[ceiling(runif(1) * length(labels))]])
}

# 1回のタスク
Task <- function(
  setting,          # ゲーム設定データフレーム
  rands.for.reward, # 各ドローの報酬決定用の乱数
  method,           # 方針: Supervised (教示的), Evaluative (評価的)
  rands.for.explore=rep(0.0, n.draw), # 評価的な方針でのみ使用
  epsilon=0.0                         # 評価的な方針でのみ使用
)
{
  # i 回目に指定のラベルのアームを引いたときの報酬
  Draw <- function(label, i) {
    return(ifelse(rands.for.reward[[i]] < setting$prob[setting$label==label], 1, 0))
  }
  # 過去に推定した正解ラベルをためる用 (教示的な方針でのみ使用)
  estimation.hist <- rep(0, nrow(setting))
  names(estimation.hist) <- setting$label
  # 過去の行動の結果をためる用 (評価的な方針でのみ使用)
  rewards.hist <- list()
  for (label in setting$label) {
    rewards.hist <- c(rewards.hist, list(c()))
  }
  names(rewards.hist) <- setting$label
  # タスク開始
  labels.all <- c()
  rewards.all <- c()
  # 最初はすべての行動を試す (仮)
  for (i.draw in 1:nrow(setting)) {
    label <- setting$label[[i.draw]]
    labels.all <- c(labels.all, label)
    reward <- Draw(label, i.draw)
    rewards.all <- c(rewards.all, reward)
    if (method == "Supervised") { # 教示的な方針のときの情報更新
      label.estimate <- ifelse(reward == 1, label, setting$label[setting$label != label])
      estimation.hist[[label.estimate]] <- estimation.hist[[label.estimate]] + 1
    } else if (method == "Evaluative") { # 評価的な方針のときの情報更新
      rewards.hist[[label]] <- c(rewards.hist[[label]], reward)
    }
  }
  # その後は方針にしたがって行動
  for (i.draw in (nrow(setting)+1):n.draw) {
    label <- NA
    if (method == "Supervised") { # 教示的な方針のときのラベル判断
      label <- get.best.label(estimation.hist)
    } else if (method == "Evaluative") { # 評価的な方針のときのラベル判断
      values <- sapply(rewards.hist, mean)
      label.optimum <- get.best.label(values)
      if (epsilon == 0) { # 利用
        label <- label.optimum
      } else {
        if (rands.for.explore[[i.draw]] < epsilon) { # 探査
          labels.cand <- setting$label[setting$label != label.optimum]
          label <- labels.cand[[ceiling(runif(1) * length(labels.cand))]]
        } else { # 利用
          label <- label.optimum
        }
      }
    }
    labels.all <- c(labels.all, label)
    reward <- Draw(label, i.draw)
    rewards.all <- c(rewards.all, reward)
    if (method == "Supervised") { # 教示的な方針のときの情報更新
      label.estimate <- ifelse(reward == 1, label, setting$label[setting$label != label])
      estimation.hist[[label.estimate]] <- estimation.hist[[label.estimate]] + 1
    } else if (method == "Evaluative") { # 評価的な方針のときの情報更新
      rewards.hist[[label]] <- c(rewards.hist[[label]], reward)
    }
  }
  return(list(labels.all, rewards.all))
}

# ゲーム設定 (36ページの図2.2のA:難しい問題)
setting <- data.frame(
  label=c("A", "B"),
  prob=c(0.1, 0.2),
  stringsAsFactors=FALSE
)
n.task <- 1000 # タスクを何セット試行するか

optimal.degree.supervised <- rep(0.0, n.draw)
optimal.degree.evaluative <- rep(0.0, n.draw)

for (i.task in 1:n.task) {
  rands.for.reward <- runif(n.draw)
  rands.for.explore <- runif(n.draw)
  
  result <- Task(setting, rands.for.reward, "Supervised")
  optimal.degree <- ifelse(result[[1]]=="B", 1, 0) # このゲーム設定では "B" が最適
  optimal.degree.supervised <- optimal.degree.supervised + optimal.degree
  
  result <- Task(setting, rands.for.reward, "Evaluative", rands.for.explore, 0.1)
  optimal.degree <- ifelse(result[[1]]=="B", 1, 0) # このゲーム設定では "B" が最適
  optimal.degree.evaluative <- optimal.degree.evaluative + optimal.degree
}

optimal.degree.supervised <- optimal.degree.supervised / n.task
optimal.degree.evaluative <- optimal.degree.evaluative / n.task

# プロット (最初の2回は決定論的にラベル選択しているのでトリム)
y.all <- c(optimal.degree.supervised[3:n.draw], optimal.degree.evaluative[3:n.draw])
plot(c(3, n.draw), c(min(y.all), max(y.all)), 
  xlab="Steps", ylab="Optimal Degree", type="n")
lines(3:n.draw, optimal.degree.supervised[3:n.draw], col="tomato", lwd=2)
lines(3:n.draw, optimal.degree.evaluative[3:n.draw], col="dodgerblue", lwd=2)