Pytorch公式チュートリアルを訳しながら学ぶ【What is Pytorch? ~ Autograd】
IT技術
Pytorch 公式チュートリアルを訳しながら学んでみる
「Pytorch」は facebook社が開発し、2016年にリリースした、オープンソース機械学習ライブラリです。
操作方法が、「NumPy」と類似していることや、「Define-by-Run」の性質を持っているのが特徴です。
世界的にも注目度が増加しているフレームワークですが、日本語でのリソースが少ないのが現状です。
英語の公式チュートリアルには、使用方法がわかりやすくまとまっているので、今回は翻訳しながらコードの実装と共に使い方を学んでいきたいと思います!
今回は「基礎」と「勾配計算」!
公式チュートリアルは、いくつかのチャプターに分かれています。
今回は、初めに取り掛かるであろう、基礎編「What is Pytorch?」と、勾配計算の「Autograd: Automatic Differentiation」について一緒に学んでいきましょう!
【公式チュートリアルはこちら】
https://pytorch.org/tutorials/
Pytorch のインストール記事はコチラ!
2020.01.20PyTorchの特徴とインストール方法PyTorchとはPyTorch(パイトーチ)とは、Pythonの機械学習ライブラリの一つで、現在最もアツいフレームワ...
Pytorch 基礎編「What is Pytorch ?」
まず一つに、「Pytorch」は「NumPy」に替わる、GPU の計算パワーを活用できる計算パッケージであるということです。
チュートリアルを進めると分かりますが、NumPy の ndarray(n次元配列)に類似する「Tensor(テンソル)」という形で計算を行い、GPU が得意とする高速な行列計算を存分に活用していきます。
とにかくコードを書きながら見ていきましょう!
Tensors:Tensor型と生成について
1from __future__ import print_function
2import torch
Pytorch で生成される Tensor は、NumPy の ndarray に似ていますが、特徴的なのは GPU を使用して高速計算を行うことができる型であるということです。
Tensor型の様々な状態の行列の生成
次のコードは、Tensor型の様々な状態の行列(以下では5x3)の生成を行っています。
1#メモリが初期化されていない行列
2x = torch.empty(5, 3)
3print(x)
#out:
tensor([[1.7876e-35, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 2.8026e-45],
[0.0000e+00, 1.1210e-44, 0.0000e+00],
[1.4013e-45, 0.0000e+00, 0.0000e+00]])
1#ランダムに初期化された行列
2x = torch.rand(5, 3)
3print(x)
#out:
tensor([[0.7213, 0.2548, 0.4610],
[0.3287, 0.0262, 0.1603],
[0.8951, 0.5536, 0.3249],
[0.1505, 0.4647, 0.1205],
[0.1879, 0.6873, 0.1848]])
ゼロ行列
以下はゼロ行列ですが、チュートリアル上ではデータ型を、long型(64bit整数)としています。
1#ゼロ行列
2x = torch.zeros(5, 3, dtype=torch.long)
3print(x)
4print(x.dtype)
#out:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
torch.int64
データから直接 Tensor を作成
データから直接 Tensor を作成できます。
1x = torch.tensor([5.5, 3])
2print(x)
#out:
tensor([5.5000, 3.0000])
または、既存の Tensor に基づいて、新たな Tensor を作成できます。
以下は、new_ones() にて1で埋められた行列を作成し、randn_like() で行列xを正規分布に従うランダムな数値に置き換えています。
また、同時にデータ型の上書きも行っています(double→float)。
1#1の行列を生成
2x = x.new_ones(5, 3, dtype=torch.double)
3print(x)
4#
5x = torch.randn_like(x, dtype=torch.float)
6#result has the same size
7print(x)
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.6357, -0.3452, 0.1283],
[-0.7312, -0.6917, 0.4404],
[ 0.3671, -0.2578, -1.6911],
[ 0.9498, -0.4784, 0.0713],
[ 0.0162, -0.8405, 0.3782]])
サイズの取得は size() で行い、randn_like() で生成したこの場合は、基の Tensor と同じです。
1print(x.size())
#out:
torch.Size([5, 3])
Operations:演算などの各種操作
各種操作について、まず加算操作から見ていきましょう。
1# +記号を用いた場合
2y = torch.rand(5, 3)
3print(x + y)
4
5#add()を用いた場合
6print(torch.add(x, y))
出力
いずれも加算処理を行っており、出力は同じです。
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
引数として渡すこともできる
出力 Tensor を、引数として渡すこともできます。
1result = torch.empty(5, 3)
2torch.add(x, y, out=result)
3print(result)
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
add_() で変数自体に加算し更新
また、add_() で変数自体に加算し、更新することができます。
このような、Tensor をその場(in-place)で変更する操作は、全て「 _ 」で表され、他にも x.copy_(y) や x.t_() などは x 自体が変更されます。
1#yにxを加えて、更新
2y.add_(x)
3print(y)
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
インデックスによる操作を行うことができる
Tensor行列は、NumPy のようなインデックスによる操作を同様に行うことができます。
1# [ :, 1 ]で参照する行と列を指定
2x = torch.randn(5, 3)
3print(x)
4print(x[:, 1])
#out:
tensor([[-0.4510, 0.2800, 0.1055],
[-0.3795, -1.1968, 0.2691],
[ 0.9133, -1.2371, 0.9872],
[ 1.8379, 1.4533, 0.4116],
[-0.5310, 1.2762, -0.5901]])
tensor([ 0.2800, -1.1968, -1.2371, 1.4533, 1.2762])
resize や reshape を行いたい場合
resize や reshape を行いたい場合は torch.view() を用います。
1x = torch.randn(4, 4)
2y = x.view(16) #16個の要素の1次元配列に変更
3z = x.view(-1, 8) #-1のサイズは他の次元によって予測されます。
4print(x.size(), y.size(), z.size()) #各Tensorのサイズを出力
5print(y)
6print(z)
#out:
#変更後のサイズ
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
#view(16)に変更後
tensor([-0.0941, -1.7758, -0.2859, -2.2382, 0.0138, 2.3766, 0.7874, 0.8522,
0.9718, -1.2113, 0.3079, 1.7919, 0.3283, 1.4063, 1.4154, 1.5530])
#view(-1, 8)に変更後
tensor([[-0.0941, -1.7758, -0.2859, -2.2382, 0.0138, 2.3766, 0.7874, 0.8522],
[ 0.9718, -1.2113, 0.3079, 1.7919, 0.3283, 1.4063, 1.4154, 1.5530]])
.item() を使用することで 数値として取得
一つの要素のみの Tensor は .item() を使用することで、数値として取得することができます。
1x = torch.randn(1)
2print(x)
3print(x.item())
#out:
tensor([-1.9968])
-1.996762990951538
その他 Tensorオペレーション
他にも多くの Tensorオペレーションが用意されており、以下で参照できるのでチェックしてみてください。
【参考サイト】
https://pytorch.org/docs/stable/torch.html
NumPy Bridge:NumPyとTorch Tensorの双方向変換
Torch Tensor と NumPy Array の相互変換を、比較的容易に行うことができます。
これは基礎となるメモリ位置を共有しており、一方を変更すると、もう一方も変更されるためです。
Torch Tensor から NumPy Array への変換
まず、Torch Tensor から NumPy Array への変換を見ていきましょう。
1# aのTensorを.numpy()により変換
2a = torch.ones(5)
3b = a.numpy()
4print(a)
5print(b)
#out: a
tensor([1., 1., 1., 1., 1.])
#b
[1. 1. 1. 1. 1.]
以下のように、Tensor が変更されると Numpy Array も変更されます。
NumPy Array が変更されても同様です。
1a.add_(1) # aに1を加える
2print(a)
3print(b)
#out:
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
NumPy Array から Torch Tensor への変換
NumPy Array から Torch Tensor への変換も同様です。
1import numpy as np
2a = np.ones(5)
3b = torch.from_numpy(a)
4np.add(a, 1, out=a)
5print(a)
6print(b)
#out:
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
CharTensor(符号付き8bit整数)を除く、CPU 上の全ての Tensor は、このように NumPy への変換、逆変換がサポートされています。
この後に出てきますが、Tensor は機械学習に適した特徴(GPUが使用可能、勾配情報の保持など)を有しています。
NumPy の ndarray型では、それらの特徴は使えないものの、状況に応じて NumPyモジュールを使い分けることが出来ます。
CUDA Tensors:デバイス間移動とGPU処理
さて、Pytorch Tensorでは .to メソッドを使用して、任意のデバイスに Tensor を移動することができ、CUDA を利用してGPU上で処理を行うことができます。
device の宣言は torch.device() を使用します。
Tensor の生成時に、引数に任意の device を渡すことで、直接 GPU 上に Tensor を生成できます。
または、すでに存在する Tensor を GPU 上に転送することができます。
1#CUDAが使用可能の場合のみ実行します
2if torch.cuda.is_available():
3 device = torch.device("cuda") #デバイスとしてcudaを宣言
4 y = torch.ones_like(x, device=device) #直接GPU上にTensorを作成
5 x = x.to(device) #'.to()'でxをGPU上に転送します。または".to("cuda")"でもOK
6 z = x + y
7 print(z)
8 print(z.to("cpu", torch.double)) #".to" で同時にデータ型も変更することができます
#out:
tensor([2.3518], device='cuda:0')
tensor([2.3518], dtype=torch.float64)
出力結果から、z は 'cuda:0' というGPU上で計算されていることがわかります。
また、.to() で "cpu" を指定することで、GPU から CPU への転送ができます。
このように、Pytorch の Tensor型は、デバイス間の転送を行い、機械学習に必要な GPU での処理を行うことができるのが特徴の一つです。
Pytorch 勾配計算「Autograd:Automatic Differentiation」
Pytorch の、ニューラルネットワークにおける中核を担うのが、自動微分(Automatic Differentiation)を行うautograd パッケージです。
自動微分とは、プログラムによって定義された任意の関数について、その導関数をアルゴリズムによって求める処理です。
この autograd パッケージは、Define-by-Run という性質を持っており、Pytorch が支持される一つの特徴といえます。
Define-by-Run とは、入力データがニューラルネットワークを流れる際に、ニューラルネットの計算構造を表す計算グラフも、同時に動的に構築する手法のことです。
Pytorch では、autograd パッケージが Tensor オブジェクトの計算を全て、追跡・記憶することで、Define-by-run を可能にしています。
Tensor:Tensorオブジェクト生成から順伝播まで
Tensorオブジェクトの生成時に、requires_grad を True に設定することで、全ての操作の追跡が開始されます。
その Tensorオブジェクトが、ネットワークに流れていく際に各地点で行った計算が、計算グラフとして構築・記憶されていきます。
計算終了後、backward() を呼び出すことで、構築された計算グラフに基づいて全ての勾配が自動的に計算され、grad 属性に蓄積されます。
コード
実際に、簡単なコードを書いていきましょう。
1x = torch.ones(2, 2, requires_grad=True)
2print(x)
#out:
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
計算
簡単な計算を行ってみます。
1y = x + 2
2print(y)
#out:
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
計算結果
計算結果の y は、grad_fn 属性を持っています。
1print(y.grad_fn)
#out:
<AddBackward0 object at 0x7fa5af3edb38>
さらに計算
さらに計算を行います。
1z = y * y * 3
2out = z.mean()
3print(z, out)
#out:
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
各演算によって、grad_fn 属性が AddBackward0 や MeanBackward0 など異なっているのがわかります。
requires_grad_() を用いてみる
また、以下のように requires_grad_() を用いると、既に存在するTensorオブジェクトの requires_grad を、その場で変更できます(デフォルトでは False)。
1a = torch.randn(2, 2)
2a = ((a * 3) / (a - 1))
3print(a.requires_grad)
4a.requires_grad_(True)
5print(a.requires_grad)
6b = (a * a).sum()
7print(b.grad_fn)
#out:
False
True
<SumBackward0 object at 0x7ff50a1a5c18>
Gradients - 逆伝播と勾配の計算 -
それでは上記で計算した、変数out について、out.backward() で誤差逆伝播法(Backpropagation)を実行します。
1out.backward()
勾配の出力には、x.grad で取得できます。
結果
実際に計算をしても確認できますが、autograd によって、今回の場合「4.5」の勾配が得られました。
1print(x.grad)
2
3#out:
4tensor([[4.5000, 4.5000],
5 [4.5000, 4.5000]])
#out:
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
チュートリアルでは、このautograd について、数学的な観点から解説されていますね。
簡単にいえば、ベクトル値関数 が与えられた場合、 の に対する勾配ベクトルを並べたものが、ヤコビ行列(Jacobian matrix : )であるということです。
さらに他の関数が与えられた場合
さらに他の関数が与えられた場合、連鎖律に従い、ベクトルとヤコビ行列の行列積を計算することで、勾配を求めることができます。
例えば、スカラー関数 の勾配が、ベクトル である時、 に対する の勾配は、以下のように の行列式で表すことができます。
autograd の活用例
他にも、autograd の活用例をみていきましょう。
1x = torch.randn(3, requires_grad=True)
2y = x * 2
3while y.data.norm() < 1000:
4 y = y * 2
5print(y)
#out:
tensor([ 319.9040, -732.8150, 1116.2251], grad_fn=<MulBackward0>)
乱数により生成された、3つの値を持つ Tensor x を生成し、これに2をかけ続けた結果が、最終的な y です。
ノルムが1000未満の間、2をかけ続けています。
この勾配も同様に、backward() で計算することができます。
ですが、今回は Tensor が3つの要素を持っているので、backward() に同じ形状の Tensor である、勾配引数を指定する必要があります(以下のgradients 変数)。
勾配引数は重みパラメータに対応している
また、この勾配引数は重みパラメータに対応しており、渡した勾配引数で重み付けされ、逆伝播を行っていきます。
1gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
2y.backward(gradients)
3print(x.grad)
#out:
tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
勾配のトラッキングを止める方法 1
では次に、勾配のトラッキングを止める方法について紹介します。
torch.no_grad() のコードブロック内で、勾配の記憶を止めることができます。
1print(x.requires_grad)
2print((x ** 2).requires_grad)
3with torch.no_grad():
4 print((x ** 2).requires_grad)
#out:
True
True
False
勾配のトラッキングを止める方法 2
もしくは、既存の requires_grad=True な Tensor に対して、detach() を使用して、新しく Tensor を生成する方法です。
これは、勾配の追跡から切り離した同じ内容の Tensor を生成することができます。
1print(x.requires_grad)
2y = x.detach()
3print(y.requires_grad)
4print(x.eq(y).all())
#out:
True
False
tensor(True)
このようにして、勾配のトラッキングのON/OFFを操作することが可能です。
さいごに
Pytorch の公式チュートリアルをなぞりながら、Pytorch で扱うTensor型オブジェクトについてや、操作方法、機械学習させるための自動微分の方法について紹介しました!
自動微分については、少ないコードで実現できますが、プラスαでどのような処理なのかについても簡単に触れました。
これらを理解することで、本格的なモデルの構築にスムーズに入れると思います!
こちらの記事もオススメ!
2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
Pytorch 公式チュートリアルを訳しながら学んでみる
「Pytorch」は facebook社が開発し、2016年にリリースした、オープンソース機械学習ライブラリです。
操作方法が、「NumPy」と類似していることや、「Define-by-Run」の性質を持っているのが特徴です。
世界的にも注目度が増加しているフレームワークですが、日本語でのリソースが少ないのが現状です。
英語の公式チュートリアルには、使用方法がわかりやすくまとまっているので、今回は翻訳しながらコードの実装と共に使い方を学んでいきたいと思います!
今回は「基礎」と「勾配計算」!
公式チュートリアルは、いくつかのチャプターに分かれています。
今回は、初めに取り掛かるであろう、基礎編「What is Pytorch?」と、勾配計算の「Autograd: Automatic Differentiation」について一緒に学んでいきましょう!
【公式チュートリアルはこちら】
https://pytorch.org/tutorials/
Pytorch のインストール記事はコチラ!
2020.01.20PyTorchの特徴とインストール方法PyTorchとはPyTorch(パイトーチ)とは、Pythonの機械学習ライブラリの一つで、現在最もアツいフレームワ...
Pytorch 基礎編「What is Pytorch ?」
まず一つに、「Pytorch」は「NumPy」に替わる、GPU の計算パワーを活用できる計算パッケージであるということです。
チュートリアルを進めると分かりますが、NumPy の ndarray(n次元配列)に類似する「Tensor(テンソル)」という形で計算を行い、GPU が得意とする高速な行列計算を存分に活用していきます。
とにかくコードを書きながら見ていきましょう!
Tensors:Tensor型と生成について
1from __future__ import print_function
2import torch
Pytorch で生成される Tensor は、NumPy の ndarray に似ていますが、特徴的なのは GPU を使用して高速計算を行うことができる型であるということです。
Tensor型の様々な状態の行列の生成
次のコードは、Tensor型の様々な状態の行列(以下では5x3)の生成を行っています。
1#メモリが初期化されていない行列
2x = torch.empty(5, 3)
3print(x)
#out:
tensor([[1.7876e-35, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 2.8026e-45],
[0.0000e+00, 1.1210e-44, 0.0000e+00],
[1.4013e-45, 0.0000e+00, 0.0000e+00]])
1#ランダムに初期化された行列
2x = torch.rand(5, 3)
3print(x)
#out:
tensor([[0.7213, 0.2548, 0.4610],
[0.3287, 0.0262, 0.1603],
[0.8951, 0.5536, 0.3249],
[0.1505, 0.4647, 0.1205],
[0.1879, 0.6873, 0.1848]])
ゼロ行列
以下はゼロ行列ですが、チュートリアル上ではデータ型を、long型(64bit整数)としています。
1#ゼロ行列
2x = torch.zeros(5, 3, dtype=torch.long)
3print(x)
4print(x.dtype)
#out:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
torch.int64
データから直接 Tensor を作成
データから直接 Tensor を作成できます。
1x = torch.tensor([5.5, 3])
2print(x)
#out:
tensor([5.5000, 3.0000])
または、既存の Tensor に基づいて、新たな Tensor を作成できます。
以下は、new_ones() にて1で埋められた行列を作成し、randn_like() で行列xを正規分布に従うランダムな数値に置き換えています。
また、同時にデータ型の上書きも行っています(double→float)。
1#1の行列を生成
2x = x.new_ones(5, 3, dtype=torch.double)
3print(x)
4#
5x = torch.randn_like(x, dtype=torch.float)
6#result has the same size
7print(x)
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.6357, -0.3452, 0.1283],
[-0.7312, -0.6917, 0.4404],
[ 0.3671, -0.2578, -1.6911],
[ 0.9498, -0.4784, 0.0713],
[ 0.0162, -0.8405, 0.3782]])
サイズの取得は size() で行い、randn_like() で生成したこの場合は、基の Tensor と同じです。
1print(x.size())
#out:
torch.Size([5, 3])
Operations:演算などの各種操作
各種操作について、まず加算操作から見ていきましょう。
1# +記号を用いた場合
2y = torch.rand(5, 3)
3print(x + y)
4
5#add()を用いた場合
6print(torch.add(x, y))
出力
いずれも加算処理を行っており、出力は同じです。
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
引数として渡すこともできる
出力 Tensor を、引数として渡すこともできます。
1result = torch.empty(5, 3)
2torch.add(x, y, out=result)
3print(result)
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
add_() で変数自体に加算し更新
また、add_() で変数自体に加算し、更新することができます。
このような、Tensor をその場(in-place)で変更する操作は、全て「 _ 」で表され、他にも x.copy_(y) や x.t_() などは x 自体が変更されます。
1#yにxを加えて、更新
2y.add_(x)
3print(y)
#out:
tensor([[-0.4647, 1.3950, -0.4282],
[ 1.8951, 0.7929, 1.8287],
[ 0.6629, 0.9020, -1.1435],
[ 1.2521, 0.6579, 1.3717],
[ 1.1686, -1.1220, 1.6196]])
インデックスによる操作を行うことができる
Tensor行列は、NumPy のようなインデックスによる操作を同様に行うことができます。
1# [ :, 1 ]で参照する行と列を指定
2x = torch.randn(5, 3)
3print(x)
4print(x[:, 1])
#out:
tensor([[-0.4510, 0.2800, 0.1055],
[-0.3795, -1.1968, 0.2691],
[ 0.9133, -1.2371, 0.9872],
[ 1.8379, 1.4533, 0.4116],
[-0.5310, 1.2762, -0.5901]])
tensor([ 0.2800, -1.1968, -1.2371, 1.4533, 1.2762])
resize や reshape を行いたい場合
resize や reshape を行いたい場合は torch.view() を用います。
1x = torch.randn(4, 4)
2y = x.view(16) #16個の要素の1次元配列に変更
3z = x.view(-1, 8) #-1のサイズは他の次元によって予測されます。
4print(x.size(), y.size(), z.size()) #各Tensorのサイズを出力
5print(y)
6print(z)
#out:
#変更後のサイズ
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
#view(16)に変更後
tensor([-0.0941, -1.7758, -0.2859, -2.2382, 0.0138, 2.3766, 0.7874, 0.8522,
0.9718, -1.2113, 0.3079, 1.7919, 0.3283, 1.4063, 1.4154, 1.5530])
#view(-1, 8)に変更後
tensor([[-0.0941, -1.7758, -0.2859, -2.2382, 0.0138, 2.3766, 0.7874, 0.8522],
[ 0.9718, -1.2113, 0.3079, 1.7919, 0.3283, 1.4063, 1.4154, 1.5530]])
.item() を使用することで 数値として取得
一つの要素のみの Tensor は .item() を使用することで、数値として取得することができます。
1x = torch.randn(1)
2print(x)
3print(x.item())
#out:
tensor([-1.9968])
-1.996762990951538
その他 Tensorオペレーション
他にも多くの Tensorオペレーションが用意されており、以下で参照できるのでチェックしてみてください。
【参考サイト】
https://pytorch.org/docs/stable/torch.html
NumPy Bridge:NumPyとTorch Tensorの双方向変換
Torch Tensor と NumPy Array の相互変換を、比較的容易に行うことができます。
これは基礎となるメモリ位置を共有しており、一方を変更すると、もう一方も変更されるためです。
Torch Tensor から NumPy Array への変換
まず、Torch Tensor から NumPy Array への変換を見ていきましょう。
1# aのTensorを.numpy()により変換
2a = torch.ones(5)
3b = a.numpy()
4print(a)
5print(b)
#out: a
tensor([1., 1., 1., 1., 1.])
#b
[1. 1. 1. 1. 1.]
以下のように、Tensor が変更されると Numpy Array も変更されます。
NumPy Array が変更されても同様です。
1a.add_(1) # aに1を加える
2print(a)
3print(b)
#out:
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
NumPy Array から Torch Tensor への変換
NumPy Array から Torch Tensor への変換も同様です。
1import numpy as np
2a = np.ones(5)
3b = torch.from_numpy(a)
4np.add(a, 1, out=a)
5print(a)
6print(b)
#out:
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
CharTensor(符号付き8bit整数)を除く、CPU 上の全ての Tensor は、このように NumPy への変換、逆変換がサポートされています。
この後に出てきますが、Tensor は機械学習に適した特徴(GPUが使用可能、勾配情報の保持など)を有しています。
NumPy の ndarray型では、それらの特徴は使えないものの、状況に応じて NumPyモジュールを使い分けることが出来ます。
CUDA Tensors:デバイス間移動とGPU処理
さて、Pytorch Tensorでは .to メソッドを使用して、任意のデバイスに Tensor を移動することができ、CUDA を利用してGPU上で処理を行うことができます。
device の宣言は torch.device() を使用します。
Tensor の生成時に、引数に任意の device を渡すことで、直接 GPU 上に Tensor を生成できます。
または、すでに存在する Tensor を GPU 上に転送することができます。
1#CUDAが使用可能の場合のみ実行します
2if torch.cuda.is_available():
3 device = torch.device("cuda") #デバイスとしてcudaを宣言
4 y = torch.ones_like(x, device=device) #直接GPU上にTensorを作成
5 x = x.to(device) #'.to()'でxをGPU上に転送します。または".to("cuda")"でもOK
6 z = x + y
7 print(z)
8 print(z.to("cpu", torch.double)) #".to" で同時にデータ型も変更することができます
#out:
tensor([2.3518], device='cuda:0')
tensor([2.3518], dtype=torch.float64)
出力結果から、z は 'cuda:0' というGPU上で計算されていることがわかります。
また、.to() で "cpu" を指定することで、GPU から CPU への転送ができます。
このように、Pytorch の Tensor型は、デバイス間の転送を行い、機械学習に必要な GPU での処理を行うことができるのが特徴の一つです。
Pytorch 勾配計算「Autograd:Automatic Differentiation」
Pytorch の、ニューラルネットワークにおける中核を担うのが、自動微分(Automatic Differentiation)を行うautograd パッケージです。
自動微分とは、プログラムによって定義された任意の関数について、その導関数をアルゴリズムによって求める処理です。
この autograd パッケージは、Define-by-Run という性質を持っており、Pytorch が支持される一つの特徴といえます。
Define-by-Run とは、入力データがニューラルネットワークを流れる際に、ニューラルネットの計算構造を表す計算グラフも、同時に動的に構築する手法のことです。
Pytorch では、autograd パッケージが Tensor オブジェクトの計算を全て、追跡・記憶することで、Define-by-run を可能にしています。
Tensor:Tensorオブジェクト生成から順伝播まで
Tensorオブジェクトの生成時に、requires_grad を True に設定することで、全ての操作の追跡が開始されます。
その Tensorオブジェクトが、ネットワークに流れていく際に各地点で行った計算が、計算グラフとして構築・記憶されていきます。
計算終了後、backward() を呼び出すことで、構築された計算グラフに基づいて全ての勾配が自動的に計算され、grad 属性に蓄積されます。
コード
実際に、簡単なコードを書いていきましょう。
1x = torch.ones(2, 2, requires_grad=True)
2print(x)
#out:
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
計算
簡単な計算を行ってみます。
1y = x + 2
2print(y)
#out:
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
計算結果
計算結果の y は、grad_fn 属性を持っています。
1print(y.grad_fn)
#out:
<AddBackward0 object at 0x7fa5af3edb38>
さらに計算
さらに計算を行います。
1z = y * y * 3
2out = z.mean()
3print(z, out)
#out:
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
各演算によって、grad_fn 属性が AddBackward0 や MeanBackward0 など異なっているのがわかります。
requires_grad_() を用いてみる
また、以下のように requires_grad_() を用いると、既に存在するTensorオブジェクトの requires_grad を、その場で変更できます(デフォルトでは False)。
1a = torch.randn(2, 2)
2a = ((a * 3) / (a - 1))
3print(a.requires_grad)
4a.requires_grad_(True)
5print(a.requires_grad)
6b = (a * a).sum()
7print(b.grad_fn)
#out:
False
True
<SumBackward0 object at 0x7ff50a1a5c18>
Gradients - 逆伝播と勾配の計算 -
それでは上記で計算した、変数out について、out.backward() で誤差逆伝播法(Backpropagation)を実行します。
1out.backward()
勾配の出力には、x.grad で取得できます。
結果
実際に計算をしても確認できますが、autograd によって、今回の場合「4.5」の勾配が得られました。
1print(x.grad)
2
3#out:
4tensor([[4.5000, 4.5000],
5 [4.5000, 4.5000]])
#out:
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
チュートリアルでは、このautograd について、数学的な観点から解説されていますね。
簡単にいえば、ベクトル値関数 が与えられた場合、 の に対する勾配ベクトルを並べたものが、ヤコビ行列(Jacobian matrix : )であるということです。
さらに他の関数が与えられた場合
さらに他の関数が与えられた場合、連鎖律に従い、ベクトルとヤコビ行列の行列積を計算することで、勾配を求めることができます。
例えば、スカラー関数 の勾配が、ベクトル である時、 に対する の勾配は、以下のように の行列式で表すことができます。
autograd の活用例
他にも、autograd の活用例をみていきましょう。
1x = torch.randn(3, requires_grad=True)
2y = x * 2
3while y.data.norm() < 1000:
4 y = y * 2
5print(y)
#out:
tensor([ 319.9040, -732.8150, 1116.2251], grad_fn=<MulBackward0>)
乱数により生成された、3つの値を持つ Tensor x を生成し、これに2をかけ続けた結果が、最終的な y です。
ノルムが1000未満の間、2をかけ続けています。
この勾配も同様に、backward() で計算することができます。
ですが、今回は Tensor が3つの要素を持っているので、backward() に同じ形状の Tensor である、勾配引数を指定する必要があります(以下のgradients 変数)。
勾配引数は重みパラメータに対応している
また、この勾配引数は重みパラメータに対応しており、渡した勾配引数で重み付けされ、逆伝播を行っていきます。
1gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
2y.backward(gradients)
3print(x.grad)
#out:
tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
勾配のトラッキングを止める方法 1
では次に、勾配のトラッキングを止める方法について紹介します。
torch.no_grad() のコードブロック内で、勾配の記憶を止めることができます。
1print(x.requires_grad)
2print((x ** 2).requires_grad)
3with torch.no_grad():
4 print((x ** 2).requires_grad)
#out:
True
True
False
勾配のトラッキングを止める方法 2
もしくは、既存の requires_grad=True な Tensor に対して、detach() を使用して、新しく Tensor を生成する方法です。
これは、勾配の追跡から切り離した同じ内容の Tensor を生成することができます。
1print(x.requires_grad)
2y = x.detach()
3print(y.requires_grad)
4print(x.eq(y).all())
#out:
True
False
tensor(True)
このようにして、勾配のトラッキングのON/OFFを操作することが可能です。
さいごに
Pytorch の公式チュートリアルをなぞりながら、Pytorch で扱うTensor型オブジェクトについてや、操作方法、機械学習させるための自動微分の方法について紹介しました!
自動微分については、少ないコードで実現できますが、プラスαでどのような処理なのかについても簡単に触れました。
これらを理解することで、本格的なモデルの構築にスムーズに入れると思います!
こちらの記事もオススメ!
2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit
おすすめ記事
immichを知ってほしい
2024.10.31