Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gyu-don/f5cc025139312ccfd39e48400018118d to your computer and use it in GitHub Desktop.
Save gyu-don/f5cc025139312ccfd39e48400018118d to your computer and use it in GitHub Desktop.
MAKE PYTHON GREAT AGAIN
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import torch\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# やりたいこと\n",
"計算グラフをぶった切って、オリジナルな微分を定義しよう(いや、そんなのやりたくねぇ。けど仕方ないんだ…)\n",
"\n",
"# 今回やること\n",
"計算グラフをぶった切る関数はいろいろ考えられると思いますが、複雑な関数だと合ってるのか合ってないのか分からないので、今回は単純な関数を文字列にしてevalで評価する関数を用意します。いくら計算が単純でも、さすがにPyTorchはそんなの面倒見きれないので、計算グラフがぶった切られることになります。\n",
"\n",
"また、そうすると自分で微分を定義しないといけないですが、今回は前進差分を使います。\n",
"$$\\frac{\\partial f(x, w)}{\\partial w} = \\frac{f(x, w + \\Delta w) - f(x, w)}{\\Delta w}$$\n",
"\n",
"# 利点\n",
"ないです。モデル作り直しましょう。本当にPyTorch使ってそれやらなきゃいけないのか考えましょう。\n",
"を使うことにします。"
]
},
{
"cell_type": "code",
"execution_count": 81,
"metadata": {},
"outputs": [],
"source": [
"# どちらも同じですが、f_strは、さすがにこんなの、PyTorchでは、微分を面倒見きれません。\n",
"# 入力は、x, wともPyTorchのTensor型を想定しています。\n",
"def f(x, w):\n",
" return 2 * x * w[0] + x**2 * w[1]\n",
"\n",
"def f_str(x, w):\n",
" return torch.tensor([eval(f'2 * {x_} * {w[0]} + {x_}**2 * {w[1]}') for x_ in x])"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([3.], grad_fn=<AddBackward0>)"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = torch.tensor([1.])\n",
"w = torch.tensor([1., 1.]).requires_grad_()\n",
"\n",
"f(x, w)"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([3.])"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = torch.tensor([1.])\n",
"w = torch.tensor([1., 1.]).requires_grad_()\n",
"\n",
"f_str(x, w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`f_str`では、grad_fnがないことに注目"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
"x = torch.tensor([1.])\n",
"w = torch.tensor([1., 1.]).requires_grad_()\n",
"\n",
"y = f(x, w)\n",
"y.backward()"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([2., 1.])"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w.grad"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`f`の計算結果は、自動微分される"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"ename": "RuntimeError",
"evalue": "element 0 of tensors does not require grad and does not have a grad_fn",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-40-9bc7a4e474c9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf_str\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m/usr/lib/python3.8/site-packages/torch/tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph)\u001b[0m\n\u001b[1;32m 196\u001b[0m \u001b[0mproducts\u001b[0m\u001b[0;34m.\u001b[0m \u001b[0mDefaults\u001b[0m \u001b[0mto\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \"\"\"\n\u001b[0;32m--> 198\u001b[0;31m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 199\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 200\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/lib/python3.8/site-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables)\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0mretain_graph\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 98\u001b[0;31m Variable._execution_engine.run_backward(\n\u001b[0m\u001b[1;32m 99\u001b[0m \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_tensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 100\u001b[0m allow_unreachable=True) # allow_unreachable flag\n",
"\u001b[0;31mRuntimeError\u001b[0m: element 0 of tensors does not require grad and does not have a grad_fn"
]
}
],
"source": [
"x = torch.tensor([1.])\n",
"w = torch.tensor([1., 1.]).requires_grad_()\n",
"\n",
"y = f_str(x, w)\n",
"y.backward() # RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"None\n"
]
}
],
"source": [
"# backwardが計算できなかったので、w.gradはNoneのまま\n",
"print(w.grad)"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [],
"source": [
"# 学習用に、データセットを作ります。\n",
"\n",
"actual_w = 1.2, -3.4\n",
"\n",
"xs = np.random.rand(200).astype(np.float32)\n",
"ys = np.array([f(x, actual_w) for x in xs], dtype=np.float32)\n",
"train_d = torch.utils.data.TensorDataset(torch.from_numpy(xs), torch.from_numpy(ys))\n",
"train_loader = torch.utils.data.DataLoader(train_d, batch_size=10)\n",
"\n",
"v_xs = np.random.rand(10).astype(np.float32)\n",
"v_ys = np.array([f(x, actual_w) for x in v_xs], dtype=np.float32)\n",
"valid_d = torch.utils.data.TensorDataset(torch.from_numpy(v_xs), torch.from_numpy(v_ys))\n",
"valid_loader = torch.utils.data.DataLoader(valid_d, batch_size=1)"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {},
"outputs": [],
"source": [
"# まずは、普通のやつのモデルを作ってみる\n",
"class Model(torch.nn.Module):\n",
" def __init__(self):\n",
" super().__init__()\n",
" self.weight = torch.nn.parameter.Parameter(torch.tensor([0., 0.]))\n",
" \n",
" def forward(self, x):\n",
" return f(x, self.weight)"
]
},
{
"cell_type": "code",
"execution_count": 104,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 tensor(0.1590, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.0512, -0.5521], requires_grad=True)\n",
"1 tensor(0.1147, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.2418, -1.0564], requires_grad=True)\n",
"2 tensor(0.0806, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.4203, -1.5229], requires_grad=True)\n",
"3 tensor(0.0569, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.5757, -1.9350], requires_grad=True)\n",
"4 tensor(0.0397, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.7089, -2.2835], requires_grad=True)\n",
"5 tensor(0.0267, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.8218, -2.5669], requires_grad=True)\n",
"6 tensor(0.0169, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.9162, -2.7894], requires_grad=True)\n",
"7 tensor(0.0097, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.9938, -2.9584], requires_grad=True)\n",
"8 tensor(0.0049, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.0559, -3.0836], requires_grad=True)\n",
"9 tensor(0.0021, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1033, -3.1755], requires_grad=True)\n",
"10 tensor(0.0007, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1373, -3.2434], requires_grad=True)\n",
"11 tensor(0.0002, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1600, -3.2940], requires_grad=True)\n",
"12 tensor(8.7163e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1746, -3.3311], requires_grad=True)\n",
"13 tensor(3.2799e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1842, -3.3569], requires_grad=True)\n",
"14 tensor(1.1896e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1905, -3.3739], requires_grad=True)\n",
"15 tensor(3.8930e-06, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1945, -3.3847], requires_grad=True)\n",
"16 tensor(1.1996e-06, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1969, -3.3913], requires_grad=True)\n",
"17 tensor(3.5391e-07, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1983, -3.3952], requires_grad=True)\n",
"18 tensor(9.6183e-08, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1991, -3.3975], requires_grad=True)\n",
"19 tensor(2.4172e-08, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1996, -3.3987], requires_grad=True)\n"
]
}
],
"source": [
"model = Model()\n",
"optimizer = torch.optim.Adam(model.parameters(), lr=0.1)\n",
"criterion = torch.nn.MSELoss()\n",
"loss_hist = []\n",
"model.train()\n",
"for epoch in range(20):\n",
" for i, (xs, l) in enumerate(train_loader):\n",
" out = model(xs)\n",
" loss = criterion(out, l)\n",
" loss_hist.append(loss)\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" print(epoch, loss, model.weight)"
]
},
{
"cell_type": "code",
"execution_count": 105,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f98b076a160>]"
]
},
"execution_count": 105,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.plot(loss_hist)"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor(1.9578e-10, grad_fn=<MseLossBackward>)\n",
"tensor(7.9192e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.8404e-08, grad_fn=<MseLossBackward>)\n",
"tensor(1.5630e-08, grad_fn=<MseLossBackward>)\n",
"tensor(3.5687e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.4232e-08, grad_fn=<MseLossBackward>)\n",
"tensor(2.1366e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.2179e-08, grad_fn=<MseLossBackward>)\n",
"tensor(2.3878e-08, grad_fn=<MseLossBackward>)\n",
"tensor(1.9628e-08, grad_fn=<MseLossBackward>)\n"
]
}
],
"source": [
"for x, y in valid_loader:\n",
" pred = model(x)\n",
" print(criterion(pred, y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"だいぶいい感じ。ノイズも何もないから、そりゃそう。"
]
},
{
"cell_type": "code",
"execution_count": 120,
"metadata": {},
"outputs": [],
"source": [
"# f_strに変えるだけではダメなことを見ていく\n",
"class Model2(torch.nn.Module):\n",
" def __init__(self):\n",
" super().__init__()\n",
" self.weight = torch.nn.parameter.Parameter(torch.tensor([0., 0.]))\n",
" \n",
" def forward(self, x):\n",
" return f_str(x, self.weight)"
]
},
{
"cell_type": "code",
"execution_count": 121,
"metadata": {},
"outputs": [
{
"ename": "RuntimeError",
"evalue": "element 0 of tensors does not require grad and does not have a grad_fn",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-121-194d051407ff>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mloss_hist2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 13\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mepoch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweight\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/lib/python3.8/site-packages/torch/tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph)\u001b[0m\n\u001b[1;32m 196\u001b[0m \u001b[0mproducts\u001b[0m\u001b[0;34m.\u001b[0m \u001b[0mDefaults\u001b[0m \u001b[0mto\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;31m`\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 197\u001b[0m \"\"\"\n\u001b[0;32m--> 198\u001b[0;31m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 199\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 200\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/usr/lib/python3.8/site-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables)\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0mretain_graph\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 98\u001b[0;31m Variable._execution_engine.run_backward(\n\u001b[0m\u001b[1;32m 99\u001b[0m \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_tensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 100\u001b[0m allow_unreachable=True) # allow_unreachable flag\n",
"\u001b[0;31mRuntimeError\u001b[0m: element 0 of tensors does not require grad and does not have a grad_fn"
]
}
],
"source": [
"model2 = Model2()\n",
"optimizer = torch.optim.Adam(model2.parameters(), lr=0.1)\n",
"criterion = torch.nn.MSELoss()\n",
"loss_hist2 = []\n",
"model2.train()\n",
"for epoch in range(20):\n",
" for i, (xs, l) in enumerate(train_loader):\n",
" out = model2(xs)\n",
" loss = criterion(out, l)\n",
" loss_hist2.append(loss)\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" print(epoch, loss, model2.weight)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"続いて、微分を定義していく。\n",
"Functionを使うこと、staticmethodとしてforward, backwardを定義することはドキュメント通り。ドキュメントになかった部分を補足していく。\n",
"\n",
"- `ctx.save_for_backward`でテンソルを保存できるが、この方法では`torch.Tensor`以外は保存できない。けれど、`ctx.なんちゃら = ...`の形で保存することができ、これは`backward`で使うことが出来る\n",
" - [Pytorch内部でも使われているテクニック](https://github.com/pytorch/pytorch/blob/master/torch/autograd/_functions/tensor.py#L11)なので、おそらく、使ってもいいんじゃないか\n",
"- `backward`で返す値は、`forward`の引数に対応している。`forward`の引数からctxを除いたものの微分結果を返していく\n",
" - 微分が必要ないもの(テンソルじゃないものや、`required_grad=True`じゃないテンソル)に対応している箇所はNoneを返せばいい\n",
"- 入力がテンソル${\\bf w} = [w_0, w_1, ..., w_{n-1}]$の場合、返す値は$$[\\sum_i\\mathrm{grad\\_output}_i\\frac{\\partial f(x_i, {\\bf w})}{\\partial w_0}, \\sum_i \\mathrm{grad\\_output}_i\\frac{\\partial f(x_i, {\\bf w})}{\\partial w_1}, ... \\sum_i\\mathrm{grad\\_output}_i\\frac{\\partial f(x_i, {\\bf w})}{\\partial w_{n-1}}]$$となる。ただし、$\\sum_i$は、入力xがミニバッチ$[x_0, x_1, ...]$で来た場合に、各々の結果を足し合わせることを言っている。`grad_output`の次元はミニバッチの大きさに対応しているので、このように結果に掛け合わせる。"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {},
"outputs": [],
"source": [
"# Functionを使う。staticmethodを使う。そのへんは、ドキュメント通り\n",
"class GeneralFunctionWithForwardDifference(torch.autograd.Function):\n",
" @staticmethod\n",
" def forward(ctx, f, xs, weight):\n",
" ys = f(xs, weight)\n",
" ctx.save_for_backward(xs, ys, weight)\n",
" ctx.f = f # 実はctxにも何かを保存できて、backwardで使える\n",
" return ys\n",
" \n",
" @staticmethod\n",
" def backward(ctx, grad_output):\n",
" xs, ys, weight = ctx.saved_tensors\n",
" f = ctx.f\n",
" dw = 0.001\n",
" diff = []\n",
" weight = weight.detach() # weightに余計な計算履歴を残さないために、detachする。\n",
" for i in range(len(weight)):\n",
" weight[i] += dw\n",
" diff.append(torch.sum(grad_output * (f(xs, weight) - ys)))\n",
" weight[i] -= dw\n",
" diff = torch.tensor(diff) / dw\n",
" return None, None, diff"
]
},
{
"cell_type": "code",
"execution_count": 128,
"metadata": {},
"outputs": [],
"source": [
"class Model2(torch.nn.Module):\n",
" def __init__(self):\n",
" super().__init__()\n",
" self.weight = torch.nn.parameter.Parameter(torch.tensor([0., 0.]))\n",
" \n",
" def forward(self, x):\n",
" # 書くのが若干めんどくさい。\n",
" return GeneralFunctionWithForwardDifference.apply(f_str, x, self.weight)"
]
},
{
"cell_type": "code",
"execution_count": 129,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 tensor(0.1590, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.0512, -0.5521], requires_grad=True)\n",
"1 tensor(0.1147, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.2418, -1.0564], requires_grad=True)\n",
"2 tensor(0.0806, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.4203, -1.5229], requires_grad=True)\n",
"3 tensor(0.0569, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.5757, -1.9350], requires_grad=True)\n",
"4 tensor(0.0397, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.7089, -2.2835], requires_grad=True)\n",
"5 tensor(0.0267, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.8217, -2.5669], requires_grad=True)\n",
"6 tensor(0.0169, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.9161, -2.7894], requires_grad=True)\n",
"7 tensor(0.0097, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 0.9938, -2.9584], requires_grad=True)\n",
"8 tensor(0.0049, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.0559, -3.0836], requires_grad=True)\n",
"9 tensor(0.0021, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1033, -3.1755], requires_grad=True)\n",
"10 tensor(0.0007, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1372, -3.2434], requires_grad=True)\n",
"11 tensor(0.0002, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1600, -3.2940], requires_grad=True)\n",
"12 tensor(8.7188e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1746, -3.3310], requires_grad=True)\n",
"13 tensor(3.2809e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1842, -3.3569], requires_grad=True)\n",
"14 tensor(1.1901e-05, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1905, -3.3739], requires_grad=True)\n",
"15 tensor(3.8952e-06, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1945, -3.3847], requires_grad=True)\n",
"16 tensor(1.1996e-06, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1969, -3.3913], requires_grad=True)\n",
"17 tensor(3.5389e-07, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1983, -3.3952], requires_grad=True)\n",
"18 tensor(9.6174e-08, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1991, -3.3975], requires_grad=True)\n",
"19 tensor(2.4199e-08, grad_fn=<MseLossBackward>) Parameter containing:\n",
"tensor([ 1.1996, -3.3987], requires_grad=True)\n"
]
}
],
"source": [
"model2 = Model2()\n",
"optimizer = torch.optim.Adam(model2.parameters(), lr=0.1)\n",
"criterion = torch.nn.MSELoss()\n",
"loss_hist2 = []\n",
"model2.train()\n",
"for epoch in range(20):\n",
" for i, (xs, l) in enumerate(train_loader):\n",
" out = model2(xs)\n",
" loss = criterion(out, l)\n",
" loss_hist2.append(loss)\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" print(epoch, loss, model2.weight)"
]
},
{
"cell_type": "code",
"execution_count": 130,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7f98950c3400>]"
]
},
"execution_count": 130,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"plt.plot(loss_hist2)"
]
},
{
"cell_type": "code",
"execution_count": 131,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor(1.9318e-10, grad_fn=<MseLossBackward>)\n",
"tensor(7.9086e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.8396e-08, grad_fn=<MseLossBackward>)\n",
"tensor(1.5653e-08, grad_fn=<MseLossBackward>)\n",
"tensor(3.5723e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.4225e-08, grad_fn=<MseLossBackward>)\n",
"tensor(2.1283e-09, grad_fn=<MseLossBackward>)\n",
"tensor(1.2192e-08, grad_fn=<MseLossBackward>)\n",
"tensor(2.3896e-08, grad_fn=<MseLossBackward>)\n",
"tensor(1.9645e-08, grad_fn=<MseLossBackward>)\n"
]
}
],
"source": [
"for x, y in valid_loader:\n",
" pred = model2(x)\n",
" print(criterion(pred, y))"
]
},
{
"cell_type": "code",
"execution_count": 132,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.legend.Legend at 0x7f989501af10>"
]
},
"execution_count": 132,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# lossを重ねて書いてみる\n",
"plt.plot(loss_hist, label=\"f\")\n",
"plt.plot(loss_hist2, label=\"f_str\")\n",
"plt.legend()"
]
},
{
"cell_type": "code",
"execution_count": 133,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor([9.3132e-08], grad_fn=<SubBackward0>)\n",
"tensor([5.9605e-08], grad_fn=<SubBackward0>)\n",
"tensor([2.9802e-08], grad_fn=<SubBackward0>)\n",
"tensor([-8.9407e-08], grad_fn=<SubBackward0>)\n",
"tensor([-2.9802e-08], grad_fn=<SubBackward0>)\n",
"tensor([2.9802e-08], grad_fn=<SubBackward0>)\n",
"tensor([8.9407e-08], grad_fn=<SubBackward0>)\n",
"tensor([-5.9605e-08], grad_fn=<SubBackward0>)\n",
"tensor([-5.9605e-08], grad_fn=<SubBackward0>)\n",
"tensor([-5.9605e-08], grad_fn=<SubBackward0>)\n"
]
}
],
"source": [
"# predの差は? → どれもほぼゼロなので、どっちのモデルもほぼ同じと言える\n",
"for x, y in valid_loader:\n",
" diff = model2(x) - model(x)\n",
" print(diff)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment