[{"data":1,"prerenderedAt":1675},["ShallowReactive",2],{"content-\u002Fcontents\u002Fpytorch-beginer":3,"surroundPost-\u002Fcontents\u002Fpytorch-beginer":1666},{"id":4,"title":5,"body":6,"createdAt":1652,"description":1653,"draft":1654,"extension":1655,"meta":1656,"navigation":73,"path":1657,"seo":1658,"stem":1659,"tags":1660,"thumbnail":1664,"updatedAt":1652,"__hash__":1665},"contents\u002Fcontents\u002Fpytorch-beginer.md","【学び直し】Pytorchの基本とMLPでMNISTの分類・可視化の実装まで",{"type":7,"value":8,"toc":1643},"minimark",[9,13,16,27,30,35,43,48,51,173,176,190,193,265,268,273,276,383,393,402,494,499,505,586,596,603,606,609,612,615,618,765,771,780,790,793,797,800,833,836,1520,1523,1527,1530,1537,1540,1543,1546,1561,1564,1589,1599,1606,1609,1614,1617,1620,1623,1639],[10,11,12],"p",{},"本記事では Pytorch を１から学び直します。",[10,14,15],{},"しばらく触っていなかったらすっかり忘れてしまって困ったのでしっかりまとめていきます。",[10,17,18,19,26],{},"進め方はまず",[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fpytorch.org\u002Ftutorials\u002Fbeginner\u002Fdeep_learning_60min_blitz.html",[24],"nofollow","公式チュートリアル","を行い、理解したことを書いていきます。",[10,28,29],{},"実行環境は Google Colaboratory です。",[31,32,34],"h2",{"id":33},"pytorch-基礎","Pytorch 基礎",[10,36,37,38,42],{},"まず、Pytorch の基本になる",[39,40,41],"code",{},"tensor","についてです。",[10,44,45,47],{},[39,46,41],{},"とは、numpy の ndarray のようなもの。ようは、任意の次元の配列を作成できるものです。",[10,49,50],{},"以下が例になります。",[52,53,58],"pre",{"className":54,"code":55,"language":56,"meta":57,"style":57},"language-py shiki shiki-themes github-dark","import torch\n\n# 初期化されていない5☓3行列\nx = torch.empty(5,3)\n\n# >> tensor([[2.2037e-35, 0.0000e+00, 3.3631e-44],\n#        [0.0000e+00,        nan, 0.0000e+00],\n#        [1.1578e+27, 1.1362e+30, 7.1547e+22],\n#        [4.5828e+30, 1.2121e+04, 7.1846e+22],\n#        [9.2198e-39, 7.0374e+22, 0.0000e+00]])\n\nprint(x.shape)\n# >> torch.Size([5, 3])\n\nprint(x[0])\n# >> tensor([2.2037e-35, 0.0000e+00, 3.3631e-44])\n\nprint(x[0][0])\n# >> tensor(1.6076e-35)\n","py","",[39,59,60,68,75,81,87,92,98,104,110,116,122,127,133,139,144,150,156,161,167],{"__ignoreMap":57},[61,62,65],"span",{"class":63,"line":64},"line",1,[61,66,67],{},"import torch\n",[61,69,71],{"class":63,"line":70},2,[61,72,74],{"emptyLinePlaceholder":73},true,"\n",[61,76,78],{"class":63,"line":77},3,[61,79,80],{},"# 初期化されていない5☓3行列\n",[61,82,84],{"class":63,"line":83},4,[61,85,86],{},"x = torch.empty(5,3)\n",[61,88,90],{"class":63,"line":89},5,[61,91,74],{"emptyLinePlaceholder":73},[61,93,95],{"class":63,"line":94},6,[61,96,97],{},"# >> tensor([[2.2037e-35, 0.0000e+00, 3.3631e-44],\n",[61,99,101],{"class":63,"line":100},7,[61,102,103],{},"#        [0.0000e+00,        nan, 0.0000e+00],\n",[61,105,107],{"class":63,"line":106},8,[61,108,109],{},"#        [1.1578e+27, 1.1362e+30, 7.1547e+22],\n",[61,111,113],{"class":63,"line":112},9,[61,114,115],{},"#        [4.5828e+30, 1.2121e+04, 7.1846e+22],\n",[61,117,119],{"class":63,"line":118},10,[61,120,121],{},"#        [9.2198e-39, 7.0374e+22, 0.0000e+00]])\n",[61,123,125],{"class":63,"line":124},11,[61,126,74],{"emptyLinePlaceholder":73},[61,128,130],{"class":63,"line":129},12,[61,131,132],{},"print(x.shape)\n",[61,134,136],{"class":63,"line":135},13,[61,137,138],{},"# >> torch.Size([5, 3])\n",[61,140,142],{"class":63,"line":141},14,[61,143,74],{"emptyLinePlaceholder":73},[61,145,147],{"class":63,"line":146},15,[61,148,149],{},"print(x[0])\n",[61,151,153],{"class":63,"line":152},16,[61,154,155],{},"# >> tensor([2.2037e-35, 0.0000e+00, 3.3631e-44])\n",[61,157,159],{"class":63,"line":158},17,[61,160,74],{"emptyLinePlaceholder":73},[61,162,164],{"class":63,"line":163},18,[61,165,166],{},"print(x[0][0])\n",[61,168,170],{"class":63,"line":169},19,[61,171,172],{},"# >> tensor(1.6076e-35)\n",[10,174,175],{},"さらにイメージをつかむために手計算したものと tensor で計算したものを突き合わせてみます。\n計算するのは以下の行列。",[10,177,178,179,182,183,186,187,189],{},"$$\n\\left",[61,180,181],{},"\n\\begin{array}{rr}\n1 & 1 \\\n1 & 1  \\\n\\end{array}\n\\right","\n\\left",[61,184,185],{},"\n\\begin{array}{rr}\n1 & 0 \\\n0 & 1  \\\n\\end{array}\n\\right","\n= \\left",[61,188,181],{},"\n$$",[10,191,192],{},"これを tensor で計算するには以下のようにします。",[52,194,196],{"className":54,"code":195,"language":56,"meta":57,"style":57},"x1 = torch.tensor([[1,1],[1,1]])\nx2 = torch.tensor([[1,0],[0,1]])\n\n# 通常の掛け算はそれぞれの位置と同じ場所をかけた値になる\ny = x1 * x2\nprint(y)\n# >> tensor([[1, 0],\n#           [0, 1]])\n\n# torch.mmで行列同士の積が計算できる\ny = torch.mm(x1, x2)\nprint(y)\n# >> tensor([[1, 1],\n#           [1, 1]])\n",[39,197,198,203,208,212,217,222,227,232,237,241,246,251,255,260],{"__ignoreMap":57},[61,199,200],{"class":63,"line":64},[61,201,202],{},"x1 = torch.tensor([[1,1],[1,1]])\n",[61,204,205],{"class":63,"line":70},[61,206,207],{},"x2 = torch.tensor([[1,0],[0,1]])\n",[61,209,210],{"class":63,"line":77},[61,211,74],{"emptyLinePlaceholder":73},[61,213,214],{"class":63,"line":83},[61,215,216],{},"# 通常の掛け算はそれぞれの位置と同じ場所をかけた値になる\n",[61,218,219],{"class":63,"line":89},[61,220,221],{},"y = x1 * x2\n",[61,223,224],{"class":63,"line":94},[61,225,226],{},"print(y)\n",[61,228,229],{"class":63,"line":100},[61,230,231],{},"# >> tensor([[1, 0],\n",[61,233,234],{"class":63,"line":106},[61,235,236],{},"#           [0, 1]])\n",[61,238,239],{"class":63,"line":112},[61,240,74],{"emptyLinePlaceholder":73},[61,242,243],{"class":63,"line":118},[61,244,245],{},"# torch.mmで行列同士の積が計算できる\n",[61,247,248],{"class":63,"line":124},[61,249,250],{},"y = torch.mm(x1, x2)\n",[61,252,253],{"class":63,"line":129},[61,254,226],{},[61,256,257],{"class":63,"line":135},[61,258,259],{},"# >> tensor([[1, 1],\n",[61,261,262],{"class":63,"line":141},[61,263,264],{},"#           [1, 1]])\n",[10,266,267],{},"ちゃんと計算できていることがわかります。",[269,270,272],"h3",{"id":271},"よく使う-tensor-データ操作方法","よく使う tensor データ操作方法",[10,274,275],{},"ここではよく使う tensor データ操作方法を紹介します。numpy に似ているものが多いので numpy 慣れしていれば余裕です。",[277,278,279,290,300,310,320,330,340,350,360,370],"ul",{},[280,281,282,285],"li",{},[39,283,284],{},"tensor.empty()",[277,286,287],{},[280,288,289],{},"空の tensor を生成",[280,291,292,295],{},[39,293,294],{},"tensor.rand()",[277,296,297],{},[280,298,299],{},"乱数で初期化された tensor を生成",[280,301,302,305],{},[39,303,304],{},"tensor.zeros()",[277,306,307],{},[280,308,309],{},"ゼロで初期化された tensor を生成",[280,311,312,315],{},[39,313,314],{},"tensor.ones",[277,316,317],{},[280,318,319],{},"1 で初期化された tensor を生成",[280,321,322,325],{},[39,323,324],{},"tensor.randn_like()",[277,326,327],{},[280,328,329],{},"引数に与えられた tensor と同じサイズの tensor を生成",[280,331,332,335],{},[39,333,334],{},"torch.arange()",[277,336,337],{},[280,338,339],{},"連続した tensor を生成",[280,341,342,345],{},[39,343,344],{},"torch.view()",[277,346,347],{},[280,348,349],{},"tensor のサイズを変更する",[280,351,352,355],{},[39,353,354],{},"torch.squeeze()",[277,356,357],{},[280,358,359],{},"次元を減らす",[280,361,362,365],{},[39,363,364],{},"torch.unsqueeze()",[277,366,367],{},[280,368,369],{},"次元を増やす",[280,371,372,375],{},[39,373,374],{},"torch.detach",[277,376,377,380],{},[280,378,379],{},"グラフから切り離す（勾配の追跡を止める）",[280,381,382],{},"自分も完全に理解したかあやしいが、誤差を伝搬させたくない場合に使う",[10,384,385,388,389,392],{},[39,386,387],{},"view","と",[39,390,391],{},"squeeze","はわかりづらいので実際に試します。",[10,394,395,396,398,399,401],{},"まずは",[39,397,387],{},"から。",[39,400,387],{},"はよく見るのでしっかりやります。",[52,403,405],{"className":54,"code":404,"language":56,"meta":57,"style":57},"x = torch.rand(2,2)\n\n# >> torch.Size([2, 2])\nprint(x.size())\n\ny = x.view(4)\n\n# >> torch.Size([4])\nprint(y.size())\n\nz = y.view(-1, 2)\n\n# >> torch.Size([2, 2])\nprint(z.size())\n\nxx = x.view(-1, 1, 1)\n\n# >> torch.Size([4, 1, 1])\nprint(xx.size())\n",[39,406,407,412,416,421,426,430,435,439,444,449,453,458,462,466,471,475,480,484,489],{"__ignoreMap":57},[61,408,409],{"class":63,"line":64},[61,410,411],{},"x = torch.rand(2,2)\n",[61,413,414],{"class":63,"line":70},[61,415,74],{"emptyLinePlaceholder":73},[61,417,418],{"class":63,"line":77},[61,419,420],{},"# >> torch.Size([2, 2])\n",[61,422,423],{"class":63,"line":83},[61,424,425],{},"print(x.size())\n",[61,427,428],{"class":63,"line":89},[61,429,74],{"emptyLinePlaceholder":73},[61,431,432],{"class":63,"line":94},[61,433,434],{},"y = x.view(4)\n",[61,436,437],{"class":63,"line":100},[61,438,74],{"emptyLinePlaceholder":73},[61,440,441],{"class":63,"line":106},[61,442,443],{},"# >> torch.Size([4])\n",[61,445,446],{"class":63,"line":112},[61,447,448],{},"print(y.size())\n",[61,450,451],{"class":63,"line":118},[61,452,74],{"emptyLinePlaceholder":73},[61,454,455],{"class":63,"line":124},[61,456,457],{},"z = y.view(-1, 2)\n",[61,459,460],{"class":63,"line":129},[61,461,74],{"emptyLinePlaceholder":73},[61,463,464],{"class":63,"line":135},[61,465,420],{},[61,467,468],{"class":63,"line":141},[61,469,470],{},"print(z.size())\n",[61,472,473],{"class":63,"line":146},[61,474,74],{"emptyLinePlaceholder":73},[61,476,477],{"class":63,"line":152},[61,478,479],{},"xx = x.view(-1, 1, 1)\n",[61,481,482],{"class":63,"line":158},[61,483,74],{"emptyLinePlaceholder":73},[61,485,486],{"class":63,"line":163},[61,487,488],{},"# >> torch.Size([4, 1, 1])\n",[61,490,491],{"class":63,"line":169},[61,492,493],{},"print(xx.size())\n",[10,495,496,498],{},[39,497,387],{},"では引数にマイナス１を入れると、そこの次元数を自動で計算してくれます。したがって、最後の例ではサイズが 2☓2 から 4☓1☓1 に変わってます。",[10,500,501,502,504],{},"次は",[39,503,391],{},"です。以下に例をのせました。",[52,506,508],{"className":54,"code":507,"language":56,"meta":57,"style":57},"# てきとうなtensorを定義\nx = torch.empty(1,5,1,3)\n\n# >> torch.Size([1, 5, 1, 3])\nprint(x.shape)\n\n# 次元削除\nx = x.squeeze()\n\n# >> torch.Size([5, 3])\nprint(x.shape)\n\n# 次元を増やす。引数の位置に増える\nx = torch.unsqueeze(x,0)\n\n# >> torch.Size([1, 5, 3])\nprint(x.shape)\n",[39,509,510,515,520,524,529,533,537,542,547,551,555,559,563,568,573,577,582],{"__ignoreMap":57},[61,511,512],{"class":63,"line":64},[61,513,514],{},"# てきとうなtensorを定義\n",[61,516,517],{"class":63,"line":70},[61,518,519],{},"x = torch.empty(1,5,1,3)\n",[61,521,522],{"class":63,"line":77},[61,523,74],{"emptyLinePlaceholder":73},[61,525,526],{"class":63,"line":83},[61,527,528],{},"# >> torch.Size([1, 5, 1, 3])\n",[61,530,531],{"class":63,"line":89},[61,532,132],{},[61,534,535],{"class":63,"line":94},[61,536,74],{"emptyLinePlaceholder":73},[61,538,539],{"class":63,"line":100},[61,540,541],{},"# 次元削除\n",[61,543,544],{"class":63,"line":106},[61,545,546],{},"x = x.squeeze()\n",[61,548,549],{"class":63,"line":112},[61,550,74],{"emptyLinePlaceholder":73},[61,552,553],{"class":63,"line":118},[61,554,138],{},[61,556,557],{"class":63,"line":124},[61,558,132],{},[61,560,561],{"class":63,"line":129},[61,562,74],{"emptyLinePlaceholder":73},[61,564,565],{"class":63,"line":135},[61,566,567],{},"# 次元を増やす。引数の位置に増える\n",[61,569,570],{"class":63,"line":141},[61,571,572],{},"x = torch.unsqueeze(x,0)\n",[61,574,575],{"class":63,"line":146},[61,576,74],{"emptyLinePlaceholder":73},[61,578,579],{"class":63,"line":152},[61,580,581],{},"# >> torch.Size([1, 5, 3])\n",[61,583,584],{"class":63,"line":158},[61,585,132],{},[10,587,588,589,591,592,595],{},"このように",[39,590,391],{},"では計算上なくても問題ない tensor の形状が",[39,593,594],{},"1","となる部分を消して次元を下げてくれます（次元という言葉は適切なのか？）。",[10,597,598,599,602],{},"逆に",[39,600,601],{},"unsqueeze","は引数の位置に次元を増やしてくれます。",[31,604,605],{"id":605},"自動微分機能",[10,607,608],{},"機械学習では勾配を計算するのに微分が必要になります。tensor ではデフォルトで勾配情報を保持する仕組みがあります。",[10,610,611],{},"今回も簡単な数式をたてて検証します。",[10,613,614],{},"$$\ny = x * w + b\n$$",[10,616,617],{},"やることは偏微分です。",[52,619,621],{"className":54,"code":620,"language":56,"meta":57,"style":57},"x = torch.tensor(1, requires_grad=True, dtype=torch.float32)\nw = torch.tensor(2, requires_grad=True, dtype=torch.float32)\nb = torch.tensor(3, requires_grad=True, dtype=torch.float32)\n\nprint(x)\nprint(w)\nprint(b)\n\ny = x * w + b\nprint(y)\n\n# 微分\nprint(y.backward())\n\n# 微分結果\nprint(x.grad)\nprint(w.grad)\nprint(b.grad)\n\n# Output\n# tensor(1., requires_grad=True)\n# tensor(2., requires_grad=True)\n# tensor(3., requires_grad=True)\n# tensor(5., grad_fn=\u003CAddBackward0>)\n# None\n# tensor(2.)\n# tensor(1.)\n# tensor(1.)\n",[39,622,623,628,633,638,642,647,652,657,661,666,670,674,679,684,688,693,698,703,708,712,718,724,730,736,742,748,754,760],{"__ignoreMap":57},[61,624,625],{"class":63,"line":64},[61,626,627],{},"x = torch.tensor(1, requires_grad=True, dtype=torch.float32)\n",[61,629,630],{"class":63,"line":70},[61,631,632],{},"w = torch.tensor(2, requires_grad=True, dtype=torch.float32)\n",[61,634,635],{"class":63,"line":77},[61,636,637],{},"b = torch.tensor(3, requires_grad=True, dtype=torch.float32)\n",[61,639,640],{"class":63,"line":83},[61,641,74],{"emptyLinePlaceholder":73},[61,643,644],{"class":63,"line":89},[61,645,646],{},"print(x)\n",[61,648,649],{"class":63,"line":94},[61,650,651],{},"print(w)\n",[61,653,654],{"class":63,"line":100},[61,655,656],{},"print(b)\n",[61,658,659],{"class":63,"line":106},[61,660,74],{"emptyLinePlaceholder":73},[61,662,663],{"class":63,"line":112},[61,664,665],{},"y = x * w + b\n",[61,667,668],{"class":63,"line":118},[61,669,226],{},[61,671,672],{"class":63,"line":124},[61,673,74],{"emptyLinePlaceholder":73},[61,675,676],{"class":63,"line":129},[61,677,678],{},"# 微分\n",[61,680,681],{"class":63,"line":135},[61,682,683],{},"print(y.backward())\n",[61,685,686],{"class":63,"line":141},[61,687,74],{"emptyLinePlaceholder":73},[61,689,690],{"class":63,"line":146},[61,691,692],{},"# 微分結果\n",[61,694,695],{"class":63,"line":152},[61,696,697],{},"print(x.grad)\n",[61,699,700],{"class":63,"line":158},[61,701,702],{},"print(w.grad)\n",[61,704,705],{"class":63,"line":163},[61,706,707],{},"print(b.grad)\n",[61,709,710],{"class":63,"line":169},[61,711,74],{"emptyLinePlaceholder":73},[61,713,715],{"class":63,"line":714},20,[61,716,717],{},"# Output\n",[61,719,721],{"class":63,"line":720},21,[61,722,723],{},"# tensor(1., requires_grad=True)\n",[61,725,727],{"class":63,"line":726},22,[61,728,729],{},"# tensor(2., requires_grad=True)\n",[61,731,733],{"class":63,"line":732},23,[61,734,735],{},"# tensor(3., requires_grad=True)\n",[61,737,739],{"class":63,"line":738},24,[61,740,741],{},"# tensor(5., grad_fn=\u003CAddBackward0>)\n",[61,743,745],{"class":63,"line":744},25,[61,746,747],{},"# None\n",[61,749,751],{"class":63,"line":750},26,[61,752,753],{},"# tensor(2.)\n",[61,755,757],{"class":63,"line":756},27,[61,758,759],{},"# tensor(1.)\n",[61,761,763],{"class":63,"line":762},28,[61,764,759],{},[10,766,767,770],{},[39,768,769],{},"requires_grad=True","とすることで微分の計算対象であると定義できます。",[10,772,773,775,776,779],{},[39,774,769],{},"された tensor で関数を作成する（上記",[39,777,778],{},"y","）と計算グラフと呼ばれるものが出来上がります。",[10,781,782,783,786,787,789],{},"この計算グラフを",[39,784,785],{},"backward()","することで",[39,788,769],{},"となっている変数の勾配が算出されます。",[10,791,792],{},"結果は x で微分した場合、w で微分した場合とそれぞれ合ってます。",[31,794,796],{"id":795},"pytorch-での機械学習の流れ","Pytorch での機械学習の流れ",[10,798,799],{},"ここからは Pytorch を使った機械学習の流れを追っていきます。基本は以下のようになると思います。",[801,802,803,806,809,812,815,818,821,824,827,830],"ol",{},[280,804,805],{},"dataset",[280,807,808],{},"dataloader",[280,810,811],{},"network",[280,813,814],{},"損失関数",[280,816,817],{},"optimaizer",[280,819,820],{},"epoch 繰り返し（学習）",[280,822,823],{},"順伝搬",[280,825,826],{},"loss, acculacy 計算",[280,828,829],{},"backward",[280,831,832],{},"重み更新",[10,834,835],{},"これを順番に書くと以下のようになります。今回はわかりやすさ重視し、ネットワークは MLP で MNIST の分類を行います。",[52,837,839],{"className":54,"code":838,"language":56,"meta":57,"style":57},"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.tensorboard import SummaryWriter\nfrom torch.utils.data import DataLoader\nimport torchvision.transforms as transforms\nfrom torchvision import datasets, transforms\n\n\nfrom datetime import datetime\n\n# colabでgoogle driveをマウントしてない場合のパス\nroot=\"content\u002F\"\n\n# dataの変換方法を定義\ntrans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n\n# dataをダウンロード\ntrain_set = datasets.MNIST(root=root, train=True, transform=trans, download=True)\ntest_set = datasets.MNIST(root=root, train=False, transform=trans, download=True)\n\n# cpuかgpuか\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\n\n# dataloaderを定義\ntrain_loader = DataLoader(train_set, batch_size=100, shuffle=True)\ntest_loader = DataLoader(test_set, batch_size=100, shuffle=False)\n\n# Networkを定義\nclass MLPNet (nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.fc1 = nn.Linear(1 * 28 * 28, 512)\n        self.fc2 =nn.Linear(512, 512)\n        self.fc3 = nn.Linear(512, 10)\n        self.dropout1=nn.Dropout2d(0.2)\n        self.dropout2=nn.Dropout2d(0.2)\n\n    def forward(self, x):\n        x = F.relu(self.fc1(x))\n        x = self.dropout1(x)\n        x = F.relu(self.fc2(x))\n        x = self.dropout2(x)\n        return F.relu(self.fc3(x))\n\nnet = MLPNet().to(device)\n\n# loss関数\ncriterion = nn.CrossEntropyLoss()\n\n# 最適化方法\noptimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)\n\n# log用フォルダを毎回生成\n# tensorboardの可視化用\nnow = datetime.now()\nlog_path = \".\u002Fruns\u002F\" + now.strftime(\"%Y%m%d-%H%M%S\") + \"\u002F\"\nprint(log_path)\n\n# tensorboard用のwriter\nwriter = SummaryWriter(log_path)\n\nepochs = 30\n\nfor epoch in range(epochs):\n    train_loss = 0\n    train_acc = 0\n    val_loss = 0\n    val_acc = 0\n\n    # train dataで訓練\n    net.train()\n    for i, (images, labels) in enumerate(train_loader):\n\n        images, labels = images.view(-1, 28*28*1).to(device), labels.to(device)\n\n        # 勾配を０にリセット\n        optimizer.zero_grad()\n\n        # 順伝搬\n        out = net(images)\n\n        # loss計算\n        loss = criterion(out, labels)\n\n        # 計算したlossとaccの値を入れる\n        train_loss += loss.item()\n        train_acc += (out.max(1)[1] == labels).sum().item()\n\n        # 誤差逆伝搬\n        loss.backward()\n\n        # 重みの更新\n        optimizer.step()\n\n        # 平均のlossとacc計算\n        avg_train_loss = train_loss \u002F len(train_loader.dataset)\n        avg_train_acc = train_acc \u002F len(train_loader.dataset)\n\n    # validation dataで評価\n    net.eval()\n\n    with torch.no_grad():\n        for (images, labels) in test_loader:\n            images, labels = images.view(-1, 28*28*1).to(device), labels.to(device)\n            out = net(images)\n            loss = criterion(out, labels)\n            val_loss += loss.item()\n            acc = (out.max(1)[1] == labels).sum()\n            val_acc += acc.item()\n    avg_val_loss = val_loss \u002F len(test_loader.dataset)\n    avg_val_acc = val_acc \u002F len(test_loader.dataset)\n\n    # print log\n    print ('Epoch [{}\u002F{}], Loss: {loss:.4f}, val_loss: {val_loss:.4f}, val_acc: {val_acc:.4f}'\n                   .format(epoch+1, epochs, loss=avg_train_loss, val_loss=avg_val_loss, val_acc=avg_val_acc))\n\n    # tensorboard用\n    writer.add_scalars('loss', {'train_loss':avg_train_loss, 'val_loss':avg_val_loss},epoch+1)\n    writer.add_scalars('accuracy', {'train_acc':avg_train_acc, 'val_acc':avg_val_acc}, epoch+1)\n\nwriter.close()\n",[39,840,841,845,850,855,860,865,870,875,880,884,888,893,897,902,907,911,916,921,925,930,935,940,944,949,954,958,963,968,973,978,984,990,996,1002,1008,1014,1020,1026,1032,1037,1043,1049,1055,1061,1067,1073,1078,1084,1089,1095,1101,1106,1112,1118,1123,1129,1135,1141,1147,1153,1158,1164,1170,1175,1181,1186,1192,1198,1204,1210,1216,1221,1227,1233,1239,1244,1250,1255,1261,1267,1272,1278,1284,1289,1295,1301,1306,1312,1318,1324,1329,1335,1341,1346,1352,1358,1363,1369,1375,1381,1386,1392,1398,1403,1409,1415,1421,1427,1433,1439,1445,1451,1457,1463,1468,1474,1480,1486,1491,1497,1503,1509,1514],{"__ignoreMap":57},[61,842,843],{"class":63,"line":64},[61,844,67],{},[61,846,847],{"class":63,"line":70},[61,848,849],{},"import torch.nn as nn\n",[61,851,852],{"class":63,"line":77},[61,853,854],{},"import torch.nn.functional as F\n",[61,856,857],{"class":63,"line":83},[61,858,859],{},"import torch.optim as optim\n",[61,861,862],{"class":63,"line":89},[61,863,864],{},"from torch.utils.tensorboard import SummaryWriter\n",[61,866,867],{"class":63,"line":94},[61,868,869],{},"from torch.utils.data import DataLoader\n",[61,871,872],{"class":63,"line":100},[61,873,874],{},"import torchvision.transforms as transforms\n",[61,876,877],{"class":63,"line":106},[61,878,879],{},"from torchvision import datasets, transforms\n",[61,881,882],{"class":63,"line":112},[61,883,74],{"emptyLinePlaceholder":73},[61,885,886],{"class":63,"line":118},[61,887,74],{"emptyLinePlaceholder":73},[61,889,890],{"class":63,"line":124},[61,891,892],{},"from datetime import datetime\n",[61,894,895],{"class":63,"line":129},[61,896,74],{"emptyLinePlaceholder":73},[61,898,899],{"class":63,"line":135},[61,900,901],{},"# colabでgoogle driveをマウントしてない場合のパス\n",[61,903,904],{"class":63,"line":141},[61,905,906],{},"root=\"content\u002F\"\n",[61,908,909],{"class":63,"line":146},[61,910,74],{"emptyLinePlaceholder":73},[61,912,913],{"class":63,"line":152},[61,914,915],{},"# dataの変換方法を定義\n",[61,917,918],{"class":63,"line":158},[61,919,920],{},"trans = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n",[61,922,923],{"class":63,"line":163},[61,924,74],{"emptyLinePlaceholder":73},[61,926,927],{"class":63,"line":169},[61,928,929],{},"# dataをダウンロード\n",[61,931,932],{"class":63,"line":714},[61,933,934],{},"train_set = datasets.MNIST(root=root, train=True, transform=trans, download=True)\n",[61,936,937],{"class":63,"line":720},[61,938,939],{},"test_set = datasets.MNIST(root=root, train=False, transform=trans, download=True)\n",[61,941,942],{"class":63,"line":726},[61,943,74],{"emptyLinePlaceholder":73},[61,945,946],{"class":63,"line":732},[61,947,948],{},"# cpuかgpuか\n",[61,950,951],{"class":63,"line":738},[61,952,953],{},"device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",[61,955,956],{"class":63,"line":744},[61,957,74],{"emptyLinePlaceholder":73},[61,959,960],{"class":63,"line":750},[61,961,962],{},"# dataloaderを定義\n",[61,964,965],{"class":63,"line":756},[61,966,967],{},"train_loader = DataLoader(train_set, batch_size=100, shuffle=True)\n",[61,969,970],{"class":63,"line":762},[61,971,972],{},"test_loader = DataLoader(test_set, batch_size=100, shuffle=False)\n",[61,974,976],{"class":63,"line":975},29,[61,977,74],{"emptyLinePlaceholder":73},[61,979,981],{"class":63,"line":980},30,[61,982,983],{},"# Networkを定義\n",[61,985,987],{"class":63,"line":986},31,[61,988,989],{},"class MLPNet (nn.Module):\n",[61,991,993],{"class":63,"line":992},32,[61,994,995],{},"    def __init__(self):\n",[61,997,999],{"class":63,"line":998},33,[61,1000,1001],{},"        super().__init__()\n",[61,1003,1005],{"class":63,"line":1004},34,[61,1006,1007],{},"        self.fc1 = nn.Linear(1 * 28 * 28, 512)\n",[61,1009,1011],{"class":63,"line":1010},35,[61,1012,1013],{},"        self.fc2 =nn.Linear(512, 512)\n",[61,1015,1017],{"class":63,"line":1016},36,[61,1018,1019],{},"        self.fc3 = nn.Linear(512, 10)\n",[61,1021,1023],{"class":63,"line":1022},37,[61,1024,1025],{},"        self.dropout1=nn.Dropout2d(0.2)\n",[61,1027,1029],{"class":63,"line":1028},38,[61,1030,1031],{},"        self.dropout2=nn.Dropout2d(0.2)\n",[61,1033,1035],{"class":63,"line":1034},39,[61,1036,74],{"emptyLinePlaceholder":73},[61,1038,1040],{"class":63,"line":1039},40,[61,1041,1042],{},"    def forward(self, x):\n",[61,1044,1046],{"class":63,"line":1045},41,[61,1047,1048],{},"        x = F.relu(self.fc1(x))\n",[61,1050,1052],{"class":63,"line":1051},42,[61,1053,1054],{},"        x = self.dropout1(x)\n",[61,1056,1058],{"class":63,"line":1057},43,[61,1059,1060],{},"        x = F.relu(self.fc2(x))\n",[61,1062,1064],{"class":63,"line":1063},44,[61,1065,1066],{},"        x = self.dropout2(x)\n",[61,1068,1070],{"class":63,"line":1069},45,[61,1071,1072],{},"        return F.relu(self.fc3(x))\n",[61,1074,1076],{"class":63,"line":1075},46,[61,1077,74],{"emptyLinePlaceholder":73},[61,1079,1081],{"class":63,"line":1080},47,[61,1082,1083],{},"net = MLPNet().to(device)\n",[61,1085,1087],{"class":63,"line":1086},48,[61,1088,74],{"emptyLinePlaceholder":73},[61,1090,1092],{"class":63,"line":1091},49,[61,1093,1094],{},"# loss関数\n",[61,1096,1098],{"class":63,"line":1097},50,[61,1099,1100],{},"criterion = nn.CrossEntropyLoss()\n",[61,1102,1104],{"class":63,"line":1103},51,[61,1105,74],{"emptyLinePlaceholder":73},[61,1107,1109],{"class":63,"line":1108},52,[61,1110,1111],{},"# 最適化方法\n",[61,1113,1115],{"class":63,"line":1114},53,[61,1116,1117],{},"optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)\n",[61,1119,1121],{"class":63,"line":1120},54,[61,1122,74],{"emptyLinePlaceholder":73},[61,1124,1126],{"class":63,"line":1125},55,[61,1127,1128],{},"# log用フォルダを毎回生成\n",[61,1130,1132],{"class":63,"line":1131},56,[61,1133,1134],{},"# tensorboardの可視化用\n",[61,1136,1138],{"class":63,"line":1137},57,[61,1139,1140],{},"now = datetime.now()\n",[61,1142,1144],{"class":63,"line":1143},58,[61,1145,1146],{},"log_path = \".\u002Fruns\u002F\" + now.strftime(\"%Y%m%d-%H%M%S\") + \"\u002F\"\n",[61,1148,1150],{"class":63,"line":1149},59,[61,1151,1152],{},"print(log_path)\n",[61,1154,1156],{"class":63,"line":1155},60,[61,1157,74],{"emptyLinePlaceholder":73},[61,1159,1161],{"class":63,"line":1160},61,[61,1162,1163],{},"# tensorboard用のwriter\n",[61,1165,1167],{"class":63,"line":1166},62,[61,1168,1169],{},"writer = SummaryWriter(log_path)\n",[61,1171,1173],{"class":63,"line":1172},63,[61,1174,74],{"emptyLinePlaceholder":73},[61,1176,1178],{"class":63,"line":1177},64,[61,1179,1180],{},"epochs = 30\n",[61,1182,1184],{"class":63,"line":1183},65,[61,1185,74],{"emptyLinePlaceholder":73},[61,1187,1189],{"class":63,"line":1188},66,[61,1190,1191],{},"for epoch in range(epochs):\n",[61,1193,1195],{"class":63,"line":1194},67,[61,1196,1197],{},"    train_loss = 0\n",[61,1199,1201],{"class":63,"line":1200},68,[61,1202,1203],{},"    train_acc = 0\n",[61,1205,1207],{"class":63,"line":1206},69,[61,1208,1209],{},"    val_loss = 0\n",[61,1211,1213],{"class":63,"line":1212},70,[61,1214,1215],{},"    val_acc = 0\n",[61,1217,1219],{"class":63,"line":1218},71,[61,1220,74],{"emptyLinePlaceholder":73},[61,1222,1224],{"class":63,"line":1223},72,[61,1225,1226],{},"    # train dataで訓練\n",[61,1228,1230],{"class":63,"line":1229},73,[61,1231,1232],{},"    net.train()\n",[61,1234,1236],{"class":63,"line":1235},74,[61,1237,1238],{},"    for i, (images, labels) in enumerate(train_loader):\n",[61,1240,1242],{"class":63,"line":1241},75,[61,1243,74],{"emptyLinePlaceholder":73},[61,1245,1247],{"class":63,"line":1246},76,[61,1248,1249],{},"        images, labels = images.view(-1, 28*28*1).to(device), labels.to(device)\n",[61,1251,1253],{"class":63,"line":1252},77,[61,1254,74],{"emptyLinePlaceholder":73},[61,1256,1258],{"class":63,"line":1257},78,[61,1259,1260],{},"        # 勾配を０にリセット\n",[61,1262,1264],{"class":63,"line":1263},79,[61,1265,1266],{},"        optimizer.zero_grad()\n",[61,1268,1270],{"class":63,"line":1269},80,[61,1271,74],{"emptyLinePlaceholder":73},[61,1273,1275],{"class":63,"line":1274},81,[61,1276,1277],{},"        # 順伝搬\n",[61,1279,1281],{"class":63,"line":1280},82,[61,1282,1283],{},"        out = net(images)\n",[61,1285,1287],{"class":63,"line":1286},83,[61,1288,74],{"emptyLinePlaceholder":73},[61,1290,1292],{"class":63,"line":1291},84,[61,1293,1294],{},"        # loss計算\n",[61,1296,1298],{"class":63,"line":1297},85,[61,1299,1300],{},"        loss = criterion(out, labels)\n",[61,1302,1304],{"class":63,"line":1303},86,[61,1305,74],{"emptyLinePlaceholder":73},[61,1307,1309],{"class":63,"line":1308},87,[61,1310,1311],{},"        # 計算したlossとaccの値を入れる\n",[61,1313,1315],{"class":63,"line":1314},88,[61,1316,1317],{},"        train_loss += loss.item()\n",[61,1319,1321],{"class":63,"line":1320},89,[61,1322,1323],{},"        train_acc += (out.max(1)[1] == labels).sum().item()\n",[61,1325,1327],{"class":63,"line":1326},90,[61,1328,74],{"emptyLinePlaceholder":73},[61,1330,1332],{"class":63,"line":1331},91,[61,1333,1334],{},"        # 誤差逆伝搬\n",[61,1336,1338],{"class":63,"line":1337},92,[61,1339,1340],{},"        loss.backward()\n",[61,1342,1344],{"class":63,"line":1343},93,[61,1345,74],{"emptyLinePlaceholder":73},[61,1347,1349],{"class":63,"line":1348},94,[61,1350,1351],{},"        # 重みの更新\n",[61,1353,1355],{"class":63,"line":1354},95,[61,1356,1357],{},"        optimizer.step()\n",[61,1359,1361],{"class":63,"line":1360},96,[61,1362,74],{"emptyLinePlaceholder":73},[61,1364,1366],{"class":63,"line":1365},97,[61,1367,1368],{},"        # 平均のlossとacc計算\n",[61,1370,1372],{"class":63,"line":1371},98,[61,1373,1374],{},"        avg_train_loss = train_loss \u002F len(train_loader.dataset)\n",[61,1376,1378],{"class":63,"line":1377},99,[61,1379,1380],{},"        avg_train_acc = train_acc \u002F len(train_loader.dataset)\n",[61,1382,1384],{"class":63,"line":1383},100,[61,1385,74],{"emptyLinePlaceholder":73},[61,1387,1389],{"class":63,"line":1388},101,[61,1390,1391],{},"    # validation dataで評価\n",[61,1393,1395],{"class":63,"line":1394},102,[61,1396,1397],{},"    net.eval()\n",[61,1399,1401],{"class":63,"line":1400},103,[61,1402,74],{"emptyLinePlaceholder":73},[61,1404,1406],{"class":63,"line":1405},104,[61,1407,1408],{},"    with torch.no_grad():\n",[61,1410,1412],{"class":63,"line":1411},105,[61,1413,1414],{},"        for (images, labels) in test_loader:\n",[61,1416,1418],{"class":63,"line":1417},106,[61,1419,1420],{},"            images, labels = images.view(-1, 28*28*1).to(device), labels.to(device)\n",[61,1422,1424],{"class":63,"line":1423},107,[61,1425,1426],{},"            out = net(images)\n",[61,1428,1430],{"class":63,"line":1429},108,[61,1431,1432],{},"            loss = criterion(out, labels)\n",[61,1434,1436],{"class":63,"line":1435},109,[61,1437,1438],{},"            val_loss += loss.item()\n",[61,1440,1442],{"class":63,"line":1441},110,[61,1443,1444],{},"            acc = (out.max(1)[1] == labels).sum()\n",[61,1446,1448],{"class":63,"line":1447},111,[61,1449,1450],{},"            val_acc += acc.item()\n",[61,1452,1454],{"class":63,"line":1453},112,[61,1455,1456],{},"    avg_val_loss = val_loss \u002F len(test_loader.dataset)\n",[61,1458,1460],{"class":63,"line":1459},113,[61,1461,1462],{},"    avg_val_acc = val_acc \u002F len(test_loader.dataset)\n",[61,1464,1466],{"class":63,"line":1465},114,[61,1467,74],{"emptyLinePlaceholder":73},[61,1469,1471],{"class":63,"line":1470},115,[61,1472,1473],{},"    # print log\n",[61,1475,1477],{"class":63,"line":1476},116,[61,1478,1479],{},"    print ('Epoch [{}\u002F{}], Loss: {loss:.4f}, val_loss: {val_loss:.4f}, val_acc: {val_acc:.4f}'\n",[61,1481,1483],{"class":63,"line":1482},117,[61,1484,1485],{},"                   .format(epoch+1, epochs, loss=avg_train_loss, val_loss=avg_val_loss, val_acc=avg_val_acc))\n",[61,1487,1489],{"class":63,"line":1488},118,[61,1490,74],{"emptyLinePlaceholder":73},[61,1492,1494],{"class":63,"line":1493},119,[61,1495,1496],{},"    # tensorboard用\n",[61,1498,1500],{"class":63,"line":1499},120,[61,1501,1502],{},"    writer.add_scalars('loss', {'train_loss':avg_train_loss, 'val_loss':avg_val_loss},epoch+1)\n",[61,1504,1506],{"class":63,"line":1505},121,[61,1507,1508],{},"    writer.add_scalars('accuracy', {'train_acc':avg_train_acc, 'val_acc':avg_val_acc}, epoch+1)\n",[61,1510,1512],{"class":63,"line":1511},122,[61,1513,74],{"emptyLinePlaceholder":73},[61,1515,1517],{"class":63,"line":1516},123,[61,1518,1519],{},"writer.close()\n",[10,1521,1522],{},"かなり基本的なコードになっています。tensorboard を使わずに matplotlib で可視化しても大丈夫です。tensorboard の使い方については以降で説明します。",[31,1524,1526],{"id":1525},"google-colabratory-で-pytorch-の-tensorboard-を使う方法","Google Colabratory で PyTorch の TensorBoard を使う方法",[10,1528,1529],{},"以前、jupyter notebook で pytorch から tensorboard を使う方法を紹介しました。",[277,1531,1532],{},[280,1533,1534],{},[20,1535,1536],{"href":57},"PyTorch で TensorBoard を使う方法",[10,1538,1539],{},"なんと Google Colabratory でも pytorch で tensorboard が使えました！",[10,1541,1542],{},"上述のコードも tensorboard での可視化を前提に書いてあるので参考にしてください。",[10,1544,1545],{},"使い方は以下のように簡単です。",[801,1547,1548,1551],{},[280,1549,1550],{},"Google Colabratory の任意のディレクトリに writer で結果を保存",[280,1552,1553,1554,388,1557,1560],{},"セルに",[39,1555,1556],{},"%load_ext tensorboard",[39,1558,1559],{},"%tensorboard --logdir .\u002Fruns\u002F\u003Cdir name>\u002F","を入力し、実行",[10,1562,1563],{},"これをセルに入れて実行すれば OK です。",[52,1565,1567],{"className":54,"code":1566,"language":56,"meta":57,"style":57},"# 2回目以降の実行時はloadではなく、以下のreloadを使う\n%load_ext tensorboard\n# %reload_ext tensorboard\n%tensorboard --logdir .\u002Fruns\u002F\u003Cdir name>\u002F\n",[39,1568,1569,1574,1579,1584],{"__ignoreMap":57},[61,1570,1571],{"class":63,"line":64},[61,1572,1573],{},"# 2回目以降の実行時はloadではなく、以下のreloadを使う\n",[61,1575,1576],{"class":63,"line":70},[61,1577,1578],{},"%load_ext tensorboard\n",[61,1580,1581],{"class":63,"line":77},[61,1582,1583],{},"# %reload_ext tensorboard\n",[61,1585,1586],{"class":63,"line":83},[61,1587,1588],{},"%tensorboard --logdir .\u002Fruns\u002F\u003Cdir name>\u002F\n",[10,1590,1591,1592,1594,1595,1598],{},"コメントに書いたように",[39,1593,1556],{},"を２回実行すると異なるポートで実行されて結果が見えなくなったので、２回目以降の実行は",[39,1596,1597],{},"%reload_ext tensorboard","を使うようにします。",[10,1600,1601,1602,1605],{},"また、",[39,1603,1604],{},"logdir","は変数ではダメだったので上記のように文字列で直接パスを書きました。",[10,1607,1608],{},"先ほどのコードの次のセルで tensorboard の可視化を行った結果が以下の画像になります。",[1610,1611],"img",{"alt":1612,"img-src":1613},"image","\u002Fimg\u002Fpytorch-beginer\u002Fpart1.png",[10,1615,1616],{},"以前 jupyter で紹介したときとは異なり、新規タブではなくセルの中に上記のような TensorBoard が現れます。",[10,1618,1619],{},"めちゃめちゃ見やすくできてます！",[31,1621,1622],{"id":1622},"参考書籍",[277,1624,1625,1632],{},[280,1626,1627],{},[20,1628,1631],{"href":1629,"rel":1630},"https:\u002F\u002Famzn.to\u002F3cK2IDV",[24],"PyTorch ニューラルネットワーク実装ハンドブック (Python ライブラリ定番セレクション)",[280,1633,1634],{},[20,1635,1638],{"href":1636,"rel":1637},"https:\u002F\u002Famzn.to\u002F2WIr4bG",[24],"つくりながら学ぶ! PyTorch による発展ディープラーニング",[1640,1641,1642],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":57,"searchDepth":70,"depth":70,"links":1644},[1645,1648,1649,1650,1651],{"id":33,"depth":70,"text":34,"children":1646},[1647],{"id":271,"depth":77,"text":272},{"id":605,"depth":70,"text":605},{"id":795,"depth":70,"text":796},{"id":1525,"depth":70,"text":1526},{"id":1622,"depth":70,"text":1622},"2020-05-17","忘れてしまったPytorchの基本を学び直す記事です。Pytorchでよくでるtensorの操作方法やMLPでMNISTの分類を行う方法を実装コードメインで紹介します。また、Google ColabratoryでTensorBoardで可視化する方法も合わせて説明します。",false,"md",{},"\u002Fcontents\u002Fpytorch-beginer",{"title":5,"description":1653},"contents\u002Fpytorch-beginer",[1661,1662,1663],"PyTorch","Python","機械学習","\u002Fimg\u002Ftwitter-card.png","DvmcHMY-t6KFdIuKu_ZpbrEacSXhnHL80-dkctkjFPU",[1667,1671],{"title":1668,"path":1669,"stem":1670,"children":-1},"【PyTorch】モデルの可視化・保存方法について学ぶ","\u002Fcontents\u002Fpytorch-advanced","contents\u002Fpytorch-advanced",{"title":1672,"path":1673,"stem":1674,"children":-1},"PyTorchの勾配更新方法の解説","\u002Fcontents\u002Fpytorch-more-adv","contents\u002Fpytorch-more-adv",1784936718545]