{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "intro",
   "metadata": {},
   "source": [
    "# 19　試しながら学ぶ強化学習\n",
    "\n",
    "FUJIMOTO LAB 深層学習コース。Web教材の図と説明を読んでから実行してください。Pythonの計算はColabの実行環境で行います。\n",
    "\n",
    "**この回の目標**：状態・行動・報酬と、探索をする理由を具体例で説明できる\n",
    "\n",
    "このノートブックは小さな合成データを使用し、元の教材の固定Driveパスや動画を必要としません。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "predict",
   "metadata": {},
   "source": [
    "## 1　まず予想する\n",
    "\n",
    "コードを実行する前に、表示される値や形を予想してください。"
   ]
  },
  {
   "cell_type": "code",
   "id": "demo-one",
   "metadata": {},
   "source": [
    "import numpy as np\n",
    "positions = np.arange(4)\n",
    "rewards = np.array([0, 0, 0, 1])\n",
    "print('位置:', positions)\n",
    "print('ゴールの報酬:', rewards[-1])"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "explain-one",
   "metadata": {},
   "source": [
    "**確かめ方**：ROBORONは位置0から出発します。位置3がゴールです。途中の報酬は0で、最後だけ1を受け取ります。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "part-two",
   "metadata": {},
   "source": [
    "## 2　行動の価値を少しずつ更新する\n",
    "\n",
    "Q値は、ある位置である行動を選ぶ良さの見積もりです。ゴールへ近づく行動が高く評価されるか、Colabで試します。"
   ]
  },
  {
   "cell_type": "code",
   "id": "demo-two",
   "metadata": {},
   "source": [
    "q = np.zeros((4, 2))  # 各位置で、左・右の2行動\n",
    "state, action, reward, next_state = 2, 1, 1, 3\n",
    "q[state, action] += 0.5 * (reward + 0.9 * q[next_state].max() - q[state, action])\n",
    "print(q[2])"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "explain-two",
   "metadata": {},
   "source": [
    "**結果を読む**：位置2で右を選び、ゴールへ着いた経験から、右のQ値が0から0.5へ上がります。左の値はまだ0です。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "experiment",
   "metadata": {},
   "source": [
    "## 3　値を変えて比べる\n",
    "\n",
    "次のセルでは、表示される値や条件を変えて、何が結果を決めるかを確かめます。長くかかる実験は、少数の合成データで行います。"
   ]
  },
  {
   "cell_type": "code",
   "id": "lab",
   "metadata": {},
   "source": [
    "# 位置2で右へ進み、ゴールの報酬を受け取った経験をもう一度使う\n",
    "before = q[2, 1]\n",
    "q[2, 1] += 0.5 * (1 + 0.9 * q[3].max() - q[2, 1])\n",
    "print('更新前:', before, '更新後:', q[2, 1])\n"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "extra-guide-1",
   "metadata": {},
   "source": [
    "## 追加実習 1　小さな道でQ学習を試す\n",
    "\n",
    "ε（イプシロン）=0.2で、ときどき別の行動も試します。左端からゴールへ進む行動の価値が上がるか観察します。\n",
    "\n",
    "**実行前に予想**：何が変わり、何が変わらないでしょうか。"
   ]
  },
  {
   "cell_type": "code",
   "id": "extra-code-1",
   "metadata": {},
   "source": [
    "rng = np.random.default_rng(7)\n",
    "q = np.zeros((4, 2))  # 行: 位置、列: 左・右\n",
    "alpha, gamma, epsilon = 0.5, 0.9, 0.2\n",
    "for episode in range(300):\n",
    "    state = 0\n",
    "    for step in range(20):\n",
    "        action = int(rng.integers(2)) if rng.random() < epsilon else int(np.argmax(q[state]))\n",
    "        next_state = max(0, state-1) if action == 0 else min(3, state+1)\n",
    "        reward = 1.0 if next_state == 3 else 0.0\n",
    "        future = 0.0 if next_state == 3 else q[next_state].max()\n",
    "        q[state, action] += alpha * (reward + gamma*future - q[state, action])\n",
    "        state = next_state\n",
    "        if state == 3:\n",
    "            break\n",
    "print('各位置での 左/右 のQ値:\\n', np.round(q[:3], 3))\n",
    "print('位置0からの道:')\n",
    "state = 0\n",
    "path = [state]\n",
    "for _ in range(6):\n",
    "    state = max(0, state-1) if np.argmax(q[state]) == 0 else min(3, state+1)\n",
    "    path.append(state)\n",
    "    if state == 3:\n",
    "        break\n",
    "print(path)\n"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "extra-reflect-1",
   "metadata": {},
   "source": [
    "**確認**：予想と違った点を一つ書き、値を一つ変えて再実行してください。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "extra-guide-2",
   "metadata": {},
   "source": [
    "## 追加実習 2　探索がないとどうなる？\n",
    "\n",
    "最初のQ値はどれも0。いつも左を選ぶ設定では、左端から動けません。試すことの役割を確認します。\n",
    "\n",
    "**実行前に予想**：何が変わり、何が変わらないでしょうか。"
   ]
  },
  {
   "cell_type": "code",
   "id": "extra-code-2",
   "metadata": {},
   "source": [
    "no_exploration = np.zeros((4, 2))\n",
    "state = 0\n",
    "for _ in range(5):\n",
    "    action = int(np.argmax(no_exploration[state]))\n",
    "    state = max(0, state-1) if action == 0 else min(3, state+1)\n",
    "print('探索しない5歩後の位置:', state)\n",
    "print('最初の同点時は左を選ぶルールなので、位置0のままです。')\n"
   ],
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "extra-reflect-2",
   "metadata": {},
   "source": [
    "**確認**：予想と違った点を一つ書き、値を一つ変えて再実行してください。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "reflection",
   "metadata": {},
   "source": [
    "## 自分の言葉で答えよう\n",
    "\n",
    "まだ道を知らないROBORONに必要なのは？\n",
    "\n",
    "- まず予想を書く\n",
    "- コードのどの行が答えを決めるか指す\n",
    "- 条件や値を1つ変えて、予想と実行結果を比べる\n",
    "\n",
    "**ヒント**：試したことのない行動を試すと、よりよい道を見つけられることがあります。"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "19-reinforcement.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
