2017年12月4日月曜日

Loss functionのあれこれ

semantic segmentationのloss functionで完全に迷子になったので復習.

回帰用

  1. torch.nn.L1Loss()

    これで学習すると重みがsparseになりやすいことが知られている.
  2. torch.nn.MSELoss

分類用

個のクラスに分類する.

  1. torch.nn.CrossEntropyLoss
    NLLとsoftmaxを合成したloss.
    minibatch として,その元次元のベクトルで,成分がクラスに分類されるスコアであるとする.スコアはによって確率に変換される.
    が分類されるべきクラス,に分類される確率とすると,

    pytorchでは,入力は
    input: のtensor. 行は.
    target: のtensor,

  2. torch.nn.NLLLoss
    Cross Entropy Lossとほとんど同じ. softmaxを噛ませるか噛ませないか.

  3. torch.nn.PoissonNLLLoss

  4. torch.nn.NLLLoss2d
    NLLLossの画像版で,inputのピクセルごとにNLLLossを計算する.
    input: のtensor. とりあえずmini-batchの次元は無視するとして,成分に対応する要素をとすると,
    がinputのピクセルがクラスに属するスコアであって,をそれが属すべき真のクラスとすると

    targetはというtensorで,成分は,

  5. torch.nn.KLDivLoss
    KL-divergenceによるloss. inputは確率分布だから,総和は1になる.

  6. torch.nn.BCELoss, binary cross entropy criterion

    不安定なので,BCEWithLogitsLossが提案されている.

  7. BCEWithLogitsLoss

    auto-encoderに使われるらしい. が必ず成立するようにする.

Semantic segmentationでは複雑なloss functionを自分で書いて実装することになる・・・

2017年11月29日水曜日

論文読み 2017, Dynamic Routing Between Capsules

元ネタ: Dynamic Routing Between Capsules, Sabour et al. NIPS, 2017 (第一著者のSara氏にもっと敬意を)

人間の視覚は極僅かな凝視点(fixation points)を選んで,その近くだけを高解像度に処理している. 例えば顔を認識する時,有る凝視点は口,またある凝視点は鼻・・・といった風に凝視点を選んでいるとしよう. この論文では,ある凝視点(口を見ている凝視点とする)を処理する時,単に口の情報だけでなくもっと多くの情報を得ていると仮定し,さらに我々の視覚はそれぞれの凝視点から得た情報を構文木のように構造化していると仮定する. ヒトの視覚はmulti-layer neural networkであることがわかっていて,さらにそれぞれのlayerは多くの”capsule”と呼ばれるneuron群に分割でき(これは多分筆者の想像),見られているものに関係したcapsuleだけが発火して構文木を構成している. 低次の(目玉に近い)layerでのcapsuleの発火に従ってより高次のlayerでのcapsuleの発火が決定されていくという処理の繰り返しが起きているのである. capsuleの中のそれぞれのneuronは見ている対象の様々な属性(位置,大きさ,方向,deformation,測度,反射,etc.)を表現している.
この論文では以上の仮定の計算モデルを実装し,それが画像処理でうまく働いたことを示した.

2017年11月27日月曜日

論文読み 2015, 2016, DeepLab v1/v2

元ネタ:
Liang-Chieh Chen and George Papandreou and Iasonas Kokkinos and Kevin Murphy and Alan L Yuille, Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs, ICLR, 2015.

Liang-Chieh Chen, George Papandreou, Iasonas Kokkinos, Kevin Murphy, Alan L. Yuille, DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, Atrous Convolution, and Fully Connected CRFs, arXiv preprint, 2016

“DeepLab”の初出とその直後に出た論文. 最新のDeepLabv3は現在のstate-of-the-artである.

DeepLabの特徴を3つ挙げる.
1. poolingによるdownsamplingのかわりにAtrous Convolution(fig.1)を使って,downsamplingのときに得られる一つ一つの特徴量がより広い範囲の入力元を反映することになる.Atrous ConvolutionはDilated Convolutionとも呼ばれる. PyTorchではtorch.nn.Conv2dのdilation引数で設定できる.
enter image description here figure 1. from vdumoulin/conv_arithmetic
2. 画面いっぱいに写っていようと画面の片隅に写っていようと猫は猫であるように,image segmentationではscale invarianceを考慮しなければならない. 著者はAtrous Convolutionのdilationを様々に設定することでこれに対処している(fig.2). 著者はこの技法を”atrous spatial pyramid pooling”(ASPP)と呼んでいる.
enter image description here
figure 2, from Liang-Chieh et al. 2016
3. bilinaer upsamplingによってfeature mapを引き伸ばすのだが,その直後にfully-connected Conditional Random Field (CRF)を用いる. CRFはdeep-learning以前のimage segmentationでよく用いられてきた技法である.画像の隣り合った画素のペアを考えて,その2つが同じobjectに入るか否かを計算していくのがUnary(Basic) CRFである(fig.3a). Fully Connecetd CRFでは文字通りある画素とその他の任意の画素のペアが同じobjectに入るか否かを考慮する(fig.3b). 当然Fully Connected CRFはそのままだと極めて計算量が大きいが,Krahenbuhl and Koltun, 2011.は平均場近似を導入して高速化を行い,exhaustiveな計算に近い結果を得た(fig.4).

enter image description here
figure 3. from https://www.cs.auckland.ac.nz/courses/compsci708s1c/lectures/glect-html/topic4c708fsc.htm
enter image description here
figure 4, from Krahenbuhl and Koltun, 2011


モデル概観, from Liang-Chieh et al. 2015

似たアーキテクチャで注目されているのにZheng et al. Conditional Random Fields as Recurrent Neural Networksがある

(詳しい議論はDeepLabv3の論文読みで)

論文読み 2015, SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation

元ネタ: Vijay Badrinarayanan, Alex Kendall, Roberto Cipolla,
“SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation.” PAMI, 2017.

(arxivで2015なのにPAMIで2017ってどういうことなの・・・)

自動運転のAIのために開発されたimage segmentation技術.
構造はU-Netによく似ている(fig.1)が,feature mapの引き伸ばしをmax-unpooling( PyTorch)によって行うことでモデルを単純化し,リアルタイムな処理に向いたアルゴリズムになっている
figure 1, SegNetウェブサイトより

論文読み 2015, U-Net: Convolutional Networks for Biomedical Image Segmentation

元ネタ: Ronneberger, Olaf; Fischer, Philipp; Brox, Thomas,
U-Net: Convolutional Networks for Biomedical Image Segmentation. Medical Image Computing and Computer-Assisted Intervention (MICCAI), Springer, LNCS, Vol.9351: 234–241, 2015

細胞や臓器といった生物学の画像に特化したimage segmentationの技法.
FCNがfeature mapの縦横を急激に縮めたり引き伸ばすのに対して,U-Netでは縮めるのは1/2倍づつ,引き伸ばすのも2倍づつとしている. またfeature mapの縦横をa倍するたびにfeature mapの数を1/a倍している. fig.1から見て取れるように,U-Netを図示するとほとんど左右対称になっていて,左側の,特徴を抽出しつつ画像を圧縮していく部分をencoder,特徴量に対応したsegmentationを保持しながら画像を引き伸ばしていく部分をdecoderという. Encoderで得られた特徴量は縦横比が一致するように切り抜かれてから対応するfeature volumeと結合し,さらにupsamplingされていく. 引き伸ばしには”up-convolution”(おそらくFCNと同じヤツ)を使う.
enter image description here

生物学的画像の特徴として,かなりグニャグニャ歪めてもその実体(細胞・臓器)はクラスとして不変だから,そのようなdata augmentationを工夫している.

わかりやすい実装, ZijunDeng

DataLoaderの使い方

DataLoaderにはDataset型を食わせればいい
例:Kaixhin/FCN-semantic-segmentation/data.py

import os
import random
from PIL import Image
import torch
from torch.utils.data import Dataset


# Labels: -1 license plate, 0 unlabeled, 1 ego vehicle, 2 rectification border, 3 out of roi, 4 static, 5 dynamic, 6 ground, 7 road, 8 sidewalk, 9 parking, 10 rail track, 11 building, 12 wall, 13 fence, 14 guard rail, 15 bridge, 16 tunnel, 17 pole, 18 polegroup, 19 traffic light, 20 traffic sign, 21 vegetation, 22 terrain, 23 sky, 24 person, 25 rider, 26 car, 27 truck, 28 bus, 29 caravan, 30 trailer, 31 train, 32 motorcycle, 33 bicycle
num_classes = 20
full_to_train = {-1: 19, 0: 19, 1: 19, 2: 19, 3: 19, 4: 19, 5: 19, 6: 19, 7: 0, 8: 1, 9: 19, 10: 19, 11: 2, 12: 3, 13: 4, 14: 19, 15: 19, 16: 19, 17: 5, 18: 19, 19: 6, 20: 7, 21: 8, 22: 9, 23: 10, 24: 11, 25: 12, 26: 13, 27: 14, 28: 15, 29: 19, 30: 19, 31: 16, 32: 17, 33: 18}
train_to_full = {0: 7, 1: 8, 2: 11, 3: 12, 4: 13, 5: 17, 6: 19, 7: 20, 8: 21, 9: 22, 10: 23, 11: 24, 12: 25, 13: 26, 14: 27, 15: 28, 16: 31, 17: 32, 18: 33, 19: 0}
full_to_colour = {0: (0, 0, 0), 7: (128, 64, 128), 8: (244, 35, 232), 11: (70, 70, 70), 12: (102, 102, 156), 13: (190, 153, 153), 17: (153, 153, 153), 19: (250, 170, 30), 20: (220, 220, 0), 21: (107, 142, 35), 22: (152, 251, 152), 23: (70, 130, 180), 24: (220, 20, 60), 25: (255, 0, 0), 26: (0, 0, 142), 27: (0, 0, 70), 28: (0, 60,100), 31: (0, 80, 100), 32: (0, 0, 230), 33: (119, 11, 32)}


class CityscapesDataset(Dataset):
  def __init__(self, split='train', crop=None, flip=False):
    super().__init__()
    self.crop = crop
    self.flip = flip
    self.inputs = []
    self.targets = []

    for root, _, filenames in os.walk(os.path.join('leftImg8bit_trainvaltest', 'leftImg8bit', split)):
      for filename in filenames:
        if os.path.splitext(filename)[1] == '.png':
          filename_base = '_'.join(filename.split('_')[:-1])
          target_root = os.path.join('gtFine_trainvaltest', 'gtFine', split, os.path.basename(root))
          self.inputs.append(os.path.join(root, filename_base + '_leftImg8bit.png'))
          self.targets.append(os.path.join(target_root, filename_base + '_gtFine_labelIds.png'))

  def __len__(self):
    return len(self.inputs)

  def __getitem__(self, i):
    # Load images and perform augmentations with PIL
    input, target = Image.open(self.inputs[i]), Image.open(self.targets[i])
    # Random uniform crop
    if self.crop is not None:
      w, h = input.size
      x1, y1 = random.randint(0, w - self.crop), random.randint(0, h - self.crop)
      input, target = input.crop((x1, y1, x1 + self.crop, y1 + self.crop)), target.crop((x1, y1, x1 + self.crop, y1 + self.crop))
    # Random horizontal flip
    if self.flip:
      if random.random() < 0.5:
        input, target = input.transpose(Image.FLIP_LEFT_RIGHT), target.transpose(Image.FLIP_LEFT_RIGHT)

    # Convert to tensors
    w, h = input.size
    input = torch.ByteTensor(torch.ByteStorage.from_buffer(input.tobytes())).view(h, w, 3).permute(2, 0, 1).float().div(255)
    target = torch.ByteTensor(torch.ByteStorage.from_buffer(target.tobytes())).view(h, w).long()
    # Normalise input
    input[0].add_(-0.485).div_(0.229)
    input[1].add_(-0.456).div_(0.224)
    input[2].add_(-0.406).div_(0.225)
    # Convert to training labels
    remapped_target = target.clone()
    for k, v in full_to_train.items():
      remapped_target[target == k] = v
    # Create one-hot encoding
    target = torch.zeros(num_classes, h, w)
    for c in range(num_classes):
      target[c][remapped_target == c] = 1
return input, target, remapped_target # Return x, y (one-hot), y (index)

2017年11月23日木曜日

論文読み 2015, Fully Convolutional Networks for Semantic Segmentation

元ネタ: Long, J., Shelhamer, E., & Darrell, T. Fully convolutional networks for semantic segmentation. In CVPR, 2015.

Introduction

CNNではレイヤーを重ねるに連れて特徴量(この論文内ではsemanticsと呼んでいる)が抽出されていくが,一方でそれが元の画像のどこに有るのか(location)という情報は失われてしまう. これはimage segmentationの場合に不利に働き,普通に出力層を元の画像と対応したピクセルの並びと考えて学習しても,極めて荒い情報しか得られない. そこで著者はFully Convolutional Net (FCN, 全ての層がconvolutionであるnet)を利用した. FCNは空間的情報を前の方のlayerから”skip architecture”によって持ってきて,semanticsと統合してpixelごとの属性を推測する.
LeNetやVGGのようなclassification用のネットワークからのtransfer learningによって学習を高速化しているのもこの論文の特徴である.

Fully convolutional networks

を(layerの)入力ののところに有るデータとし,を(layerの)出力のそれとすると,convolutionやそれにともなうpoolingやactivationのlayer (stageと呼んだほうが通りがいいか)というのは,

と書ける.ただしはfilterの幅,はストライドの幅とする.
複数のlayerの合成の表現は

と書ける.したがって,全結合層を持たないCNNは(これをFully Convolutional Netowork, FCNという)この合成の形で書けることになる. また,FCNは任意の大きさの画像を入力と出来る.

Adapting classifiers for dense prediction

ふつうのCNNの最後の方には全結合層が有るが,これを,その直前のlayerからの入力(h x w x d)を,h x w x dのfilterでconvolutionしたものだと考えることが出来る(fig.1).
しかしこれでは出力層では空間的情報が著しく失われているので,出力層をdense(密)にするというのが著者の重要な仕事といえる.

Upsampling is backwards strided convolution

enter image description here
(vdumoulin/conv_arithmetic, MIT license)

Segmentation Architectue

Combining what and where

enter image description here
空間的情報を前の方のlayerから”skip architecture”によって持ってきて,semanticsと統合してpixelごとの属性を推測する
skip architectureがどうなっているのかわからないので今度pytorchを見る
(ただの足し算だった)

2017年11月22日水曜日

論文読み 2015, Rethinking the Inception Architecture for Computer Vision

元ネタ: Szegedy, Christian & Vanhoucke, Vincent & Ioffe, Sergey & Shlens, Jon & Wojna, ZB. (2015). Rethinking the Inception Architecture for Computer Vision. . 10.1109/CVPR.2016.308.

Introduction

Inception1を改良して,Inception-v2並びにv3を開発した.

Inception-v3はInception-v2のAuxiliary classifierにbatch-normalizationを追加したモデルと言える.

General Design Principles

Inception2の改良を試みた際に得たいくつかの経験則を挙げている.
1. layerを急に狭める(representational bottleneck)ことを避ける. ネットワークを通して結局は同じ次元に落とし込むにしても,あるlayerの次のlayerに移る時に急に次元がさげてはならない.
2. 高次元のlayerは局所的に処理しやすい. convoluationにおいてtileごとのactivationを増加させると,抽出される特徴がよりdisentangledになる.
3. 低次元な埋め込みによって,空間的なaggregationをほとんどあるいは全くロスなくおこなえる.例えば(3x3)のconvolutionを行う前に1x1のconvolutionでそのlayerへの入力を低次元に埋め込み,より効率的に3x3のconvolutionを行えて,かつ情報のロスはわずかである.
4. ネットワークの幅と深さのバランスをとる. 片方を増加させることでNNの性能を向上させることは可能だが,療法を同時に増加させることで計算量の増大を抑えつつ性能を向上させられる.

Factorizing Convolutions with Larger Filter Size

Inception3では1x1のconvolutionで次元削減した上で5x5や3x3のconvolutionを行ったが,他のfactorizing convolutionを試している.

3.1 Factorization into smaller convolutions

5x5 や 7x7のconvolutionは非常に計算のコストが大きいので,より小さいconvolutionを複数重ねることで似たようなconvolutionを実現する.(fig.1)
enter image description here

3.2 Spatial Factorization into Asymmetric Convolutions

nxnのconvolutionを1xnとnx1のconvolutionの2段構えで処理する(fig.2)と計算効率は飛躍的に向上する.実験では,lower layers(inputに近いlayer)でこれを行うとうまく行かないが,12x12や20x20のconvolutionでは非常に効果的であった.

4. Utility of Auxiliary Classifiers

Inception4で複数のclassifierを導入して勾配消失を防ごうとしたが,Lee et al[^2]と対照的に著者は複数のclassifierによる収束の高速化は確認できなかった一方で,classifierが一つであるよりも僅かに分類性能の向上を認めた.著者らは複数のclassifierがregularizerとして働いているのではないかと考えている.

5. Efficient Grid Size Reduction

fig.2の方法でfeature mapを効率的に増やしているらしい
enter image description here
figure 2

7. Model Regularization via Label Smoothing (LSR)

training setにおけるラベルの分布を,example とは関係なく得る(traing setにおけるの出現確率かと思ったがそうでもないらしい),またsmoothing parameter を定義する. ground truth のexample に対して,ground truthの分布:(ディラックのデルタ)を,
によって書き換える.
例えばとすれば

である.


  1. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014
  2. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014
  3. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014
  4. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014

2017年11月21日火曜日

論文読み 2014, Going Deeper with Convolutions

Introduction

Szegedy et al. (2014)1ではより深いDNNを実現するためにInception moduleを導入し,22層からなるGoogLenetを開発し,ほぼ同じ計算量で性能の向上を実現している.VGGNet2は精度の点ではGoogLeNetと同程度だが,従来のCNNを深くしただけで,非常に計算量が大きいという欠点が有る.
ただ単にDNNを大きくすることは計算量を増大させると同時に必要なtraining dataを莫大にしてしまう. これを克服するためにsparsityを用いるのだが,sparse行列の演算は現在の計算機では扱いづらい. 従来は”spatial domain”においてconvolutionを導入してsparsityを実現してきたが,著者は”filter-level”でsparsityを実現し,しかも複数のsparse行列の演算を1つのdense行列の演算として近似して効率よく計算する方法を開発した.
Inception moduleとは通常のconvolutionの代わりに挿入される,複数のconvolutionをひとまとめにしたレイヤーと言える.
CNNでは局所的な統計量のクラスターが前のレイヤーから後ろのレイヤーに渡されていくわけだが,その受け渡されたレイヤーでももとの局所性は保存されているから,ついにはその局所的な統計量たちの殆どがfeature mapのうちの1x1の部分のそれぞれに格納されることになる. Inception moduleでは1x1のフィルタに加え,1x1の部分に収まりきらない局所統計量をとらえるため,3x3と5x5のフィルタも加え,ついでにpoolingも加えている(fig.1.a), .
大量のfeature mapが有る上に5X5のconvolutionを行うのは非常に計算が重いので,1x1のconvolutionを適当な枚数のfeature mapを作るように掛けて次元圧縮してからそれに3x3や5x5のconvolutionを施す(fig.1.b), (table1, n5x5redやn3x3redが次元圧縮した後の次元数).
計算上の理由から,Inception moduleはDNNのhigher layerにおいてのみ挿入される. GoogLeNetは合計で22のlayerからなるが,当時としてはあまりに深いネットワークなので,中頃の層にもclassifierを追加して(fig.2)勾配消失を軽減するとともに,regularizationを実現していると著者は考えている.

enter image description here
figure1

Table1, PyTorch実装,kuangliu, MIT license

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.autograd import Variable


class Inception(nn.Module):
    def __init__(self, in_planes, n1x1, n3x3red, n3x3, n5x5red, n5x5, pool_planes):
        super(Inception, self).__init__()
        # 1x1 conv branch
        self.b1 = nn.Sequential(
            nn.Conv2d(in_planes, n1x1, kernel_size=1),
            nn.BatchNorm2d(n1x1),
            nn.ReLU(True),
        )

        # 1x1 conv -> 3x3 conv branch
        self.b2 = nn.Sequential(
            nn.Conv2d(in_planes, n3x3red, kernel_size=1),
            nn.BatchNorm2d(n3x3red),
            nn.ReLU(True),
            nn.Conv2d(n3x3red, n3x3, kernel_size=3, padding=1),
            nn.BatchNorm2d(n3x3),
            nn.ReLU(True),
        )

        # 1x1 conv -> 5x5 conv branch
        self.b3 = nn.Sequential(
            nn.Conv2d(in_planes, n5x5red, kernel_size=1),
            nn.BatchNorm2d(n5x5red),
            nn.ReLU(True),
            nn.Conv2d(n5x5red, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
            nn.Conv2d(n5x5, n5x5, kernel_size=3, padding=1),
            nn.BatchNorm2d(n5x5),
            nn.ReLU(True),
        )

        # 3x3 pool -> 1x1 conv branch
        self.b4 = nn.Sequential(
            nn.MaxPool2d(3, stride=1, padding=1),
            nn.Conv2d(in_planes, pool_planes, kernel_size=1),
            nn.BatchNorm2d(pool_planes),
            nn.ReLU(True),
        )

    def forward(self, x):
        y1 = self.b1(x)
        y2 = self.b2(x)
        y3 = self.b3(x)
        y4 = self.b4(x)
        return torch.cat([y1,y2,y3,y4], 1)


class GoogLeNet(nn.Module):
    def __init__(self):
        super(GoogLeNet, self).__init__()
        self.pre_layers = nn.Sequential(
            nn.Conv2d(3, 192, kernel_size=3, padding=1),
            nn.BatchNorm2d(192),
            nn.ReLU(True),
        )

        self.a3 = Inception(192,  64,  96, 128, 16, 32, 32)
        self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)

        self.maxpool = nn.MaxPool2d(3, stride=2, padding=1)

        self.a4 = Inception(480, 192,  96, 208, 16,  48,  64)
        self.b4 = Inception(512, 160, 112, 224, 24,  64,  64)
        self.c4 = Inception(512, 128, 128, 256, 24,  64,  64)
        self.d4 = Inception(512, 112, 144, 288, 32,  64,  64)
        self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)

        self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)
        self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)

        self.avgpool = nn.AvgPool2d(8, stride=1)
        self.linear = nn.Linear(1024, 10)

    def forward(self, x):
        out = self.pre_layers(x)
        out = self.a3(out)
        out = self.b3(out)
        out = self.maxpool(out)
        out = self.a4(out)
        out = self.b4(out)
        out = self.c4(out)
        out = self.d4(out)
        out = self.e4(out)
        out = self.maxpool(out)
        out = self.a5(out)
        out = self.b5(out)
        out = self.avgpool(out)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

# net = GoogLeNet()
# x = torch.randn(1,3,32,32)
# y = net(Variable(x))
# print(y.size())

enter image description here
figure 2

figureは原論文より


  1. Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich, Going Deeper with Convolutions, In CVPR, 2014
  2. K. Simonyan and A. Zisserman. Very deep convolutionalnetworks for large-scale image recognition.arXiv preprint arXiv:1409.1556, 2014.

2017年10月18日水曜日

PyTorch練習 04日目

Autoencoderの実装.

import numpy as np
import matplotlib.pyplot as plt
import torchvision
import torch
import torch.nn as nn
from torch.autograd import Variable
from PIL import Image
import torch.utils.data as Data

# Mnist digits dataset
DOWNLOAD_MNIST = True
BATCH_SIZE = 100
train_data = torchvision.datasets.MNIST(
    root='./mnist/',
    train=True,                                     # this is training data
    transform=torchvision.transforms.ToTensor(),    # Converts a PIL.Image or numpy.ndarray to
                                                    # torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0]
    download=DOWNLOAD_MNIST,                        # download it if you don't have it
)


train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)

# convert test data into Variable, pick 2000 samples to speed up testing
test_data = torchvision.datasets.MNIST(root='./mnist/', train=False)
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[:2000]/255.   # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:2000]

class Encoder(nn.Module):
    def __init__(self):
        super(Encoder, self).__init__()
        self.fc = nn.Sequential(
            nn.Linear(784, 1000),
            nn.BatchNorm1d(1000),
            nn.ReLU(),
            nn.Linear(1000, 500),
            nn.BatchNorm1d(500),
            nn.ReLU(),
            nn.Linear(500, 250),
            nn.BatchNorm1d(250),
            nn.ReLU(),
            nn.Linear(250, 2)
        )
    def forward(self, x):
        return self.fc(x)


class Decoder(nn.Module):
    def __init__(self):
        super(Decoder, self).__init__()
        self.fc = nn.Sequential(
            nn.Linear(2, 250),
            nn.BatchNorm1d(250),
            nn.ReLU(),
            nn.Linear(250, 500),
            nn.BatchNorm1d(500),
            nn.ReLU(),
            nn.Linear(500, 1000),
            nn.BatchNorm1d(1000),
            nn.ReLU(),
            nn.Linear(1000, 784)
        )
    def forward(self, x):
        return self.fc(x)

class AutoEncoder(nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        self.fc1 = Encoder()
        self.fc2 = Decoder()

    def forward(self, x):
        code = self.fc1(x)
        out = self.fc2(code)
        return out, code

ae = AutoEncoder().cuda()
print(ae)
optimizer = torch.optim.Adam(ae.parameters(), 0.001)
loss_fn = nn.MSELoss()

plt.clf()
test_losses = []
x = test_x.view(-1, 28*28).cuda()
ar_raw = 255 * x.cpu()[0].view(28, 28).data.numpy()
fig = plt.figure(figsize=[12, 6])
ax1 = fig.add_subplot(1, 4, 1)
ax2 = fig.add_subplot(1, 4, 2)
ax3 = fig.add_subplot(1, 4, 3)
ax4 = fig.add_subplot(1, 4, 4)
ax1.imshow(255 * x.cpu()[0].view(28, 28).data.numpy(), cmap='gray')
ax2.imshow(255 * x.cpu()[1].view(28, 28).data.numpy(), cmap='gray')
ax3.imshow(255 * x.cpu()[2].view(28, 28).data.numpy(), cmap='gray')
ax4.imshow(255 * x.cpu()[3].view(28, 28).data.numpy(), cmap='gray')
plt.title('original')
plt.show()
ls = [0, 1, 2, 3, 5, 10, 20]

for epoch in range(201):
    if True:
        ae.eval()

        x = test_x.view(-1, 28*28).cuda()
        out, code = ae(x)
        loss = loss_fn(out, x)
        test_losses.append(loss.data[0])
        if epoch in ls:
            fig = plt.figure(figsize=[12, 6])
            ax1 = fig.add_subplot(1, 4, 1)
            ax2 = fig.add_subplot(1, 4, 2)
            ax3 = fig.add_subplot(1, 4, 3)
            ax4 = fig.add_subplot(1, 4, 4)
            ax1.imshow(255 * out.cpu()[0].view(28, 28).data.numpy(), cmap='gray')
            ax2.imshow(255 * out.cpu()[1].view(28, 28).data.numpy(), cmap='gray')
            ax3.imshow(255 * out.cpu()[2].view(28, 28).data.numpy(), cmap='gray')
            ax4.imshow(255 * out.cpu()[3].view(28, 28).data.numpy(), cmap='gray')
            plt.title("epoch: {}, loss: {}".format(epoch, loss.data[0]))
            plt.plot()
            plt.show()


    ae.train()
    for step, (x, y) in enumerate(train_loader):
        x = Variable(x.view(-1, 28*28)).cuda()
        out, code = ae(x)
        loss = loss_fn(out, x)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

plt.plot(test_losses)
plt.title('test_losses')
plt.show()

->

AutoEncoder (
  (fc1): Encoder (
    (fc): Sequential (
      (0): Linear (784 -> 1000)
      (1): BatchNorm1d(1000, eps=1e-05, momentum=0.1, affine=True)
      (2): ReLU ()
      (3): Linear (1000 -> 500)
      (4): BatchNorm1d(500, eps=1e-05, momentum=0.1, affine=True)
      (5): ReLU ()
      (6): Linear (500 -> 250)
      (7): BatchNorm1d(250, eps=1e-05, momentum=0.1, affine=True)
      (8): ReLU ()
      (9): Linear (250 -> 2)
    )
  )
  (fc2): Decoder (
    (fc): Sequential (
      (0): Linear (2 -> 250)
      (1): BatchNorm1d(250, eps=1e-05, momentum=0.1, affine=True)
      (2): ReLU ()
      (3): Linear (250 -> 500)
      (4): BatchNorm1d(500, eps=1e-05, momentum=0.1, affine=True)
      (5): ReLU ()
      (6): Linear (500 -> 1000)
      (7): BatchNorm1d(1000, eps=1e-05, momentum=0.1, affine=True)
      (8): ReLU ()
      (9): Linear (1000 -> 784)
    )
  )
)

enter image description here
enter image description here
enter image description here
enter image description here
enter image description here
enter image description here
enter image description here
enter image description here

2017年10月16日月曜日

imageJによるstack tiff(16bit)の変換

imageJでstackされたtiffをもらって画像に切り出そうとしたがimagemagickでうまく読めなかったのでpythonを使って変換したメモ.
Pillowsという画像処理ライブラリをによってtiffを読んで,フレームごとに8bitに変換してbmpに保存していく.

from PIL import Image
import numpy as np
im = Image.open('xxx.tif')
print(im.format, im.size, im.mode, im.n_frames)

for i in range(im.n_frames):
    im.seek(i)
    ar = np.array(im)
    ar = ar / 2**4
    pilImg = Image.fromarray(np.uint8(ar))
    pilImg.save("x_{0:04d}.bmp".format(i))

2017年9月28日木曜日

PyTorch練習 03日目

MorvanZhou/PyTorch-Tutorialを参考にMNISTの数字判別MLPを組んだ (元ネタはCNN).


import torch 
import torchvision
import torch.nn as nn
import numpy as np
import torch.utils.data as data
import torchvision.transforms as transforms
import torchvision.datasets as dsets
from torch.autograd import Variable
import matplotlib.pyplot as plt
import torch.nn.functional as F

EPOCH = 3
BATCH_SIZE = 50
LR = 0.001


transforms = torchvision.transforms.Compose([
                torchvision.transforms.ToTensor()
                ])
train_data = torchvision.datasets.MNIST(
    root='./', train=True, download=True, transform=transforms)



train_loader = torch.utils.data.DataLoader(
    dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)

test_data = torchvision.datasets.MNIST(root='./', train=False)
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1)
                 .view(-1, 28*28), volatile=True).type(torch.FloatTensor)[:2000]/255
test_y = test_data.test_labels[:2000]

mlp = torch.nn.Sequential(
            torch.nn.Linear(28*28, 100),
            torch.nn.ReLU(),
            torch.nn.Linear(100, 30),
            torch.nn.ReLU(),
            torch.nn.Linear(30, 10)
)

print(mlp)

optimizer = torch.optim.Adam(mlp.parameters(), lr=LR)
loss_fn = nn.CrossEntropyLoss()

for epoch in range(EPOCH):
    for step, (x, y) in enumerate(train_loader):
        b_x = Variable(x.view(50, 28*28))
        b_y = Variable(y)

        output = mlp(b_x)

        loss = loss_fn(output, b_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if step % 100 == 0:
            test_output = mlp(test_x)
            pred_y = torch.max(test_output, 1)[1].data.squeeze()
            accuracy = sum(pred_y == test_y) / float(test_y.size(0))
            print('Epoch: ', epoch, '| train loss: %.4f' % loss.data[0],
                 '| test accuracy: %.2f' % accuracy)
MLP (
  (layer1): Sequential (
    (0): Linear (784 -> 100)
    (1): ReLU ()
    (2): Linear (100 -> 30)
    (3): ReLU ()
    (4): Linear (30 -> 10)
  )
)
Epoch:  0 | train loss: 2.3134 | test accuracy: 0.09
Epoch:  0 | train loss: 0.7347 | test accuracy: 0.81
Epoch:  0 | train loss: 0.4593 | test accuracy: 0.85
Epoch:  0 | train loss: 0.4268 | test accuracy: 0.88
Epoch:  0 | train loss: 0.1728 | test accuracy: 0.89
Epoch:  0 | train loss: 0.2370 | test accuracy: 0.89
Epoch:  0 | train loss: 0.0994 | test accuracy: 0.90
Epoch:  0 | train loss: 0.2626 | test accuracy: 0.90
Epoch:  0 | train loss: 0.1483 | test accuracy: 0.91
Epoch:  0 | train loss: 0.2041 | test accuracy: 0.92
Epoch:  0 | train loss: 0.1486 | test accuracy: 0.91
Epoch:  0 | train loss: 0.2538 | test accuracy: 0.92
Epoch:  1 | train loss: 0.0768 | test accuracy: 0.93
Epoch:  1 | train loss: 0.1138 | test accuracy: 0.93
Epoch:  1 | train loss: 0.1675 | test accuracy: 0.93
Epoch:  1 | train loss: 0.0724 | test accuracy: 0.93
Epoch:  1 | train loss: 0.0983 | test accuracy: 0.94
Epoch:  1 | train loss: 0.1681 | test accuracy: 0.93
Epoch:  1 | train loss: 0.1569 | test accuracy: 0.94
Epoch:  1 | train loss: 0.2666 | test accuracy: 0.94
Epoch:  1 | train loss: 0.1030 | test accuracy: 0.93
Epoch:  1 | train loss: 0.1784 | test accuracy: 0.93
Epoch:  1 | train loss: 0.2013 | test accuracy: 0.95
Epoch:  1 | train loss: 0.1681 | test accuracy: 0.95
Epoch:  2 | train loss: 0.0487 | test accuracy: 0.95
Epoch:  2 | train loss: 0.0959 | test accuracy: 0.95
Epoch:  2 | train loss: 0.1366 | test accuracy: 0.95
Epoch:  2 | train loss: 0.1528 | test accuracy: 0.95
Epoch:  2 | train loss: 0.0860 | test accuracy: 0.95
Epoch:  2 | train loss: 0.0218 | test accuracy: 0.95
Epoch:  2 | train loss: 0.1122 | test accuracy: 0.95
Epoch:  2 | train loss: 0.1109 | test accuracy: 0.96
Epoch:  2 | train loss: 0.0879 | test accuracy: 0.96
Epoch:  2 | train loss: 0.1182 | test accuracy: 0.96
Epoch:  2 | train loss: 0.0585 | test accuracy: 0.96
Epoch:  2 | train loss: 0.0579 | test accuracy: 0.95

DataLoaderの使い方がまだわかっていないので引き続き練習する.
また,numpy.Array.reshapeと似た機能をもつまた,Torch.Tensor.viewについていくつかメモしておく.
この変換はtorchvision.datasets.MNISTのtransformの段階で行ったほうが効率がいいと思うが,どうやればいいのかわからなかった.

A = torch.arange(0, 5* 4 * 3 * 2) # 長さ120の一次元Tensor
A.view(5, 4, 3, 2)      # size (5, 4, 3, 2) の4次元Tensor
A.view(5, -1)           # size (5, 24) の二次元Tensor
                        # -1を引数にすると,他の次元を勘案してその次元のsizeが決まる.
A.view(7, -1)           
# RuntimeError: invalid argument 2: size '[7 x -1]' is invalid for input of with 120 elements at /pytorch/torch/lib/TH/THStorage.c:37

2017年9月27日水曜日

PyTorch練習 02日目 2

単純な線形回帰をPyTorchで実装する.

import numpy as np
import torch
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

torch.manual_seed(1)
class LinearRegression(object):
    """
    二乗誤差をloss functionとした線形回帰

    methods:
        fit(X, y, lr, n_iter) fit linear model
            X: data matrix(numpy.array)
            y: data matrix(numpy.array)
            lr: learning rate, defalt 1e-3
            n_iter: number of iteration, default 5000
        get_params() get parameters for this estimator
            return: 
        predict(X) predict using the linear model
            X: data matrix(numpy.array) 
    """

    def __init__(self):
        pass

    def fit(self, X, y, lr=1e-3, n_iter=5000):
        X = Variable(torch.from_numpy(X).float())      # 何かエラーが起きたら手当り次第
        y = Variable(torch.from_numpy(y).float())      # flaot()にすると解決するかも
        self.model = torch.nn.Sequential(              # 線形レイヤー1枚を線形回帰の
            torch.nn.Linear(X.size()[1], y.size()[1])) # パラメータ学習器にする
        loss_fn = torch.nn.MSELoss(size_average=False)
        optimizer = torch.optim.Adam(self.model.parameters(), lr=lr)

        for t in range(n_iter):
            y_pred = self.model(X)
            loss = loss_fn(y_pred, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

    def get_params(self):
        try:
            return list(model.parameters())
        except ValueError:
            print("We need to fit any data first")

    def predict(self,X):
        X = Variable(torch.from_numpy(X).float())
        try:
            return self.model(X)
        except ValueError:
            print("We need to fit any data first")
x1 = np.linspace(-1, 1, 100)
x2 = np.linspace(-1, 1, 100)
X = np.vstack((x1, x2)).T
y = 2 * x1 + 1 * x2 + 0.5 * np.random.randn(100) + 1
y = y.reshape([100, 1])

lr = LinearRegression()
lr.fit(X, y)

fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, projection='3d')


ax.scatter(x1, x2, y, label='data')
ax.scatter(x1, x2, lr.predict(X).data.numpy(), label='prediction')
plt.legend()
plt.show()

enter image description here

X = np.linspace(-1, 1, 100).reshape([100, 1])
y = 2 * X + 0.5 * np.random.randn(100, 1)
lr = LinearRegression()
lr.fit(X, y)
plt.scatter(X, y, label='data')
plt.scatter(X, lr.predict(X).data.numpy(), label='predict')
plt.legend()
plt.show()

enter image description here

PyTorch練習 02日目

MIT License
元ネタ: jcjohnson/pytorch-examples

PyTorch: Tensors

# Program 1 素朴なMLPの実装
import torch
dtype = torch.FloatTensor          # CPU上の32-bit floating point
# dtype = torch.cuda.FloatTensor   # GPU上の32-bit floating point

# D_in x H x D_outの三層ニューラルネットを構成する.
N, D_in, H, D_out = 64, 1000, 100, 10

# データセットの作成
x = torch.randn(N, D_in).type(dtype)
y = torch.randn(N, D_out).type(dtype)

# 重み行列の作成
w1 = torch.randn(D_in, H).type(dtype)
w2 = torch.randn(H, D_out).type(dtype)

# Gradient Descentによる最適化
learning_rate = 1e-6
for t in range(500):                 
    h = x.mm(w1)                     # matrix multiplication
    h_relu = h.clamp(min=0)          # Clamp all elements in input into the range [min, max] and return a resulting Tensor.
    y_pred = h_relu.mm(w2)

    # compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 50 == 0:
        print(t, loss)

    # backpropagation
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred) # a.t() : transpose
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h<0] = 0
    grad_w1 = x.t().mm(grad_h)

    # gradient descent
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2
0 33983926.96143705
50 11801.846684516087
100 357.2751016973617
150 17.174204719908438
200 1.006447628455322
250 0.06717062689924691
300 0.005198379904488515
350 0.0006403423317780793
400 0.0001579846401805196
450 6.24626183654553e-05

PyTorch: Variables and autograd

Program 1では手動でbackpropagationを行って勾配を計算したが,PyTorchでは変数をVariable型でラップすることで,その変数に対する写像の勾配を簡単に計算できる.

# Program 2 自動微分を使った三層MLPの実装
import torch
from torch.autograd import Variable
dtype = torch.FloatTensor          # CPU上の32-bit floating point
# dtype = torch.cuda.FloatTensor   # GPU上の32-bit floating point

# D_in x H x D_outの三層ニューラルネットを構成する.
N, D_in, H, D_out = 64, 1000, 100, 10

# データセットの作成
# データセットについての勾配は不要なので,requires_grad=Falseとする.
x = Variable(torch.randn(N, D_in).type(dtype), requires_grad=False)
y = Variable(torch.randn(N, D_out).type(dtype), requires_grad=False)

# 重み行列の作成
# 重み行列でloss functionを最適化するため,重み行列による勾配が必要. requires_grad=True
w1 = Variable(torch.randn(D_in, H).type(dtype), requires_grad=True)
w2 = Variable(torch.randn(H, D_out).type(dtype), requires_grad=True)

# GDによる最適化
learning_rate = 1e-6
for t in range(500):
    # backpropagationを手動ではしないので,中間の計算結果は保存しない.
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    loss = (y_pred - y).pow(2).sum()  # lossはVariableに対する演算の結果なのでVariable
                                      # VariableはTensorであり,実数は(1,)のTensorとして格納され,
                                      # v.data[0]によってただの実数として取り出せる.
    if t % 50 == 0:
        print(loss.data[0])

    # backward passの前に手動で勾配を0にする (前のループでの勾配を初期化するということ?)
    # もとの例ではt=0の場合にも初期化を行っていたが,最新のPyTorchだとエラーになる.
    if t: 
        w1.grad.data.zero_()
        w2.grad.data.zero_()

    # loss.backward()によって,lossに至るまでの全ての変数に対する勾配を計算する.
    # w1.grad, w2.gradで計算した勾配を読める.
    loss.backward()

    w1.data -= learning_rate * w1.grad.data
    w2.data -= learning_rate * w2.grad.data
33022228.0
13424.4697265625
475.697021484375
26.867782592773438
1.9564745426177979
0.1696002036333084
0.016552181914448738
0.0019943274091929197
0.0004035909951198846
0.00013585695705842227

PyTorch: nn

autogradよりも抽象的で,大規模なnetworkの記述に向いたツールにnn パッケージが有る. neural networkはいくつものlayerが重なって構成されており,層ごとにlearnable parametersをもつことがある. nnにはModuleの集合が定義されていて,それぞれがlayerの抽象化と考えることが出来る. ModuleはVariableのインスタンス(たち)を与えられてVariableのインスタンス(たち)を返し,かつlearnable parametersを含む内部状態をVariableのインスタンスとして持つ. また,nnは有用なloss functionを予め定義している. Program 3はnnによる3層のneural networkの例である.

# Program 3
import torch
from torch.autograd import Variable

N, D_in, H, D_out = 64, 1000, 100, 10

x = Variable(torch.randn(N, D_in))
y = Variable(torch.randn(N, D_out), requires_grad=False)


# torch.nn.Sequential() の引数にlayerたちを記述し,その順番にlayerを繋げたnetworkがモデルとなる.
# layerたちのもつパラメータmodelに保存されていて,あとで勾配を計算できる.
# 引数にmoduleを入れてネストした構造を作ることも出来る. 
# moduleを組み合わせてmodelすなわちnetworkを構成する.
model = torch.nn.Sequential(
        torch.nn.Linear(D_in, H),
        torch.nn.ReLU(),
        torch.nn.Linear(H, D_out),
)

# torch.nnは代表的なloss functionを予め定義している. ここではMSEを使う.
loss_fn = torch.nn.MSELoss(size_average=False)

learning_rate = 1e-4
for t in range(500):
    # Forward pass
    # modelは関数であるかのように呼べて,ここではxを引数とする.
    y_pred = model(x)

    # lossを計算する. y_predにmodelの情報(networkの全てのパラメータ)が保存されているから
    # あとでloss.backward()をするだけでnetworkのパラメータによるlossの勾配が計算される.
    # loss_fn()の返り値はまたVariableのインスタンス.
    loss = loss_fn(y_pred, y)
    if t % 50 == 0:
        print(t, loss.data[0])

    # gradientの初期化
    if t:
        model.zero_grad()

    loss.backward() # これによってgradientが計算される.

    # gradient descentの更新.
    # model.parameters()がモデルの全てのパラメータである.
    # modelのパラメータはVariableだから,以下のように更新する. 
    for param in model.parameters():
        param.data -= learning_rate * param.grad.data
0 697.54541015625
50 30.74203872680664
100 2.117093086242676
150 0.22655823826789856
200 0.03121182695031166
250 0.005095028784126043
300 0.0009658429189585149
350 0.0002087247121380642
400 4.9990881962003186e-05
450 1.284706377191469e-05

PyTorch:optim

これまではGradient Descentによって最適化を行ってきたが,より洗練された最適化手法は多くあるが,いちいち実装していては大変なので,optim packageは有用な最適化手法を定義している. Program 4はAdam methodによる最適化をoptimによって実現している.

# Program 4
import torch
from torch.autograd import Variable

N, D_in, H, D_out = 64, 1000, 100, 10
x = Variable(torch.randn(N, D_in))
y = Variable(torch.randn(N, D_out))

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

loss_fn = torch.nn.MSELoss(size_average=False)

# optimによってOptimizerを定義する. ここではAdamを使うが,ほかにも多くの手法が定義されている.
# torch.optim.Adamの最初に与える引数が,最適化を行う変数の集合である.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
    y_pred = model(x)

    loss = loss_fn(y_pred, y)
    if t % 50 == 0:
        print(t, loss.data[0])

    optimizer.zero_grad() # これはわざわざtが0出ないことを確かめなくてもエラーを起こさない

    loss.backward()

    # optimizerのstep()メソッドによって,最適化するパラメータを1回更新する.
    optimizer.step()
0 668.1607055664062
50 200.53623962402344
100 51.36286163330078
150 8.457684516906738
200 0.8371148705482483
250 0.06822004914283752
300 0.005305096507072449
350 0.0003715920902322978
400 2.1446359824039973e-05
450 9.826173936744453e-07

PyTorch: Custom nn Modules

nn.Moduleのサブクラスとして新たなmoduleを定義できる.
それには,与えられた入力(Variableのインスタンス)をautogradの演算で望む出力(Variableのインスタンス)に変換する写像をforwardとして実装する. Program 5は3層のネットワークを一つのmoduleとして定義して作ったnetworkの実装例である.

# Program 5
import torch
from torch.autograd import Variable

class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        """
        constuctorにおいて2つのnn.Linearをインスタンス化して,Variableの一員とする
        """
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred

N, D_in, H, D_out = 64, 1000, 100, 10

x = Variable(torch.randn(N, D_in))
y = Variable(torch.randn(N, D_out), requires_grad=False)

model = TwoLayerNet(D_in, H, D_out)

# loss functionとopimizerを定義する. 
criterion = torch.nn.MSELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
    y_pred = model(x)

    loss = criterion(y_pred, y)
    if t % 50 == 0:
        print(t, loss.data[0])

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
0 719.2647705078125
50 33.10668182373047
100 2.121030569076538
150 0.22128432989120483
200 0.030644414946436882
250 0.005116437561810017
300 0.00095479900483042
350 0.00019043088832404464
400 3.9648610254516825e-05
450 8.519250513927545e-06

2017年9月26日火曜日

PyTorch練習 01日目

PyTorchは人気上昇中のニューラルネットワークライブラリ. numpyと親和性が高い.

from __future__ import print_function
import numpy as np
import torch
from torch.autograd import Variable

PyTorchの変数はtorch.Tensor型として定義し,numpy.arrayと似た振る舞いをする.

x = torch.Tensor(5, 3) # メモリは初期化されない
print(x)
 0.0000e+00  0.0000e+00  5.7260e+31
 4.5801e-41  2.6699e+09  4.5801e-41
-1.3889e-14  3.0737e-41 -1.3889e-14
 3.0737e-41  3.3887e+37  4.5801e-41
 3.2915e+37  4.5801e-41  1.7148e+29
[torch.FloatTensor of size 5x3]
torch.rand(5, 3) #[0, 1]上の一様分布
 0.0090  0.0027  0.8066
 0.7704  0.8939  0.8752
 0.2019  0.2329  0.0885
 0.9281  0.5321  0.4206
 0.4536  0.0725  0.3961
[torch.FloatTensor of size 5x3]
torch.randn(5, 3) # 標準正規分布
 0.9565 -0.0533 -0.1352
 0.7636  0.0229  1.2910
 0.2379 -0.4774 -1.2879
 0.3506 -0.2690  0.9901
 0.1157 -0.3185 -0.0382
[torch.FloatTensor of size 5x3]
torch.ones(5, 3)
 1  1  1
 1  1  1
 1  1  1
 1  1  1
 1  1  1
[torch.FloatTensor of size 5x3]
torch.zeros(5, 3)
 0  0  0
 0  0  0
 0  0  0
 0  0  0
 0  0  0
[torch.FloatTensor of size 5x3]
torch.diag(torch.arange(0, 5))
 0  0  0  0  0
 0  1  0  0  0
 0  0  2  0  0
 0  0  0  3  0
 0  0  0  0  4
[torch.FloatTensor of size 5x5]
torch.eye(5)
 1  0  0  0  0
 0  1  0  0  0
 0  0  1  0  0
 0  0  0  1  0
 0  0  0  0  1
[torch.FloatTensor of size 5x5]
print(x[:, 1])  # インデクシングもpythonと変わらない
 0.0000e+00
 2.6699e+09
 3.0737e-41
 3.3887e+37
 4.5801e-41
[torch.FloatTensor of size 5]
a = np.ones(5)           # numpy.arrayからtorch.Tensorへの変換
b = torch.from_numpy(a)  # このときaに加えた変更はbに遺伝する
np.add(a, 1, out=a)      # 忘れていると痛い目にあいそう
print(a)
print(b)
[ 2.  2.  2.  2.  2.]

 2
 2
 2
 2
 2
[torch.DoubleTensor of size 5]
torch.cuda.is_available() # cudaが使えるか判定
True
x = x.cuda() # .cuda()によってGPUメモリ上に変数を移動できる.
x
 0.0000e+00  0.0000e+00  5.7260e+31
 4.5801e-41  2.6699e+09  4.5801e-41
-1.3889e-14  3.0737e-41 -1.3889e-14
 3.0737e-41  3.3887e+37  4.5801e-41
 3.2915e+37  4.5801e-41  1.7148e+29
[torch.cuda.FloatTensor of size 5x3 (GPU 0)]

自動微分

PyTorchでは勾配計算をするときは変数をtorch.autograd.Variable型に入れる.
Variableのインスタンスはrequires_gradvolatileの二つのフラグを持っていて,これらのフラグをもとに勾配計算に置いて考慮しないくていいsubgraphを除外し,効率的な計算を実現している.

requires_grad

変数が関数の引数となるとき,もまたによって勾配計算が出来るべきで,逆に言えば,の勾配を計算できなくてもよいのはの全てが勾配を計算しなくても良いときである.

x = Variable(torch.ones(2, 2), requires_grad=True)
y = 2 * x
z = y * y * 3
print(y.requires_grad, z.requires_grad)
True True
x = Variable(torch.ones(2, 2), requires_grad=False)
y = 2 * x
z = sum(sum(y))
print(y.requires_grad, z.requires_grad)
False False

と関数があって,と書けるとする.
がrequires_gradであるときもrequires_gradであって,.backward()としてから.gradとすると,が得られる. また,で,.backward()としてから.gradとするとが得られる.

x = Variable(torch.ones(2, 2), requires_grad=True)
y = 2 * x
z = y * y * 3
out = z.mean()
out.backward()
print(y.backward)
<bound method Variable.backward of Variable containing:
 2  2
 2  2
[torch.FloatTensor of size 2x2]
>
print(x.backward)
<bound method Variable.backward of Variable containing:
 1  1
 1  1
[torch.FloatTensor of size 2x2]
>
x = Variable(torch.ones(2, 2), requires_grad=True)
y = 2 * x
z = y * y * 3
out = z.mean()
z.backward()      # zが多次元なためにエラー
x.grad            
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

<ipython-input-20-c3877ab6bd13> in <module>()
      3 z = y * y * 3
      4 out = z.mean()
----> 5 z.backward()      # zが多次元なためにエラー
      6 x.grad


/usr/local/lib/python3.5/dist-packages/torch/autograd/variable.py in backward(self, gradient, retain_graph, create_graph, retain_variables)
    154                 Variable.
    155         """
--> 156         torch.autograd.backward(self, gradient, retain_graph, create_graph, retain_variables)
    157 
    158     def register_hook(self, hook):


/usr/local/lib/python3.5/dist-packages/torch/autograd/__init__.py in backward(variables, grad_variables, retain_graph, create_graph, retain_variables)
     84         grad_variables = list(grad_variables)
     85 
---> 86     grad_variables, create_graph = _make_grads(variables, grad_variables, create_graph)
     87 
     88     if retain_variables is not None:


/usr/local/lib/python3.5/dist-packages/torch/autograd/__init__.py in _make_grads(outputs, grads, user_create_graph)
     32             if out.requires_grad:
     33                 if out.numel() != 1:
---> 34                     raise RuntimeError("grad can be implicitly created only for scalar outputs")
     35                 data = out.data
     36                 new_grads.append(


RuntimeError: grad can be implicitly created only for scalar outputs