はじめに

今回の記事では、これから深層学習(機械学習)をおこないたいという方向けにPythonのライブラリであるPytorchについて紹介していきます。

Pytorchは深層学習をおこなう上で欠かせないツールであるため、記事の内容は確実にマスターしておきたいものです。

インストール方法から基本的なモデリング方法を学び、実際にコマンドを入力出来るところまで勉強していきましょう。


Pytorchとは

Pytorchとは、深層学習に適したPythonライブラリの一種です。

ベースとなるライブラリ「Torch」と「Python」を組み合わせたため「Python」+「Torch」で「Pytorch(パイトーチ)」と呼ばれています。

直感的にコードを書くことが出来ること、動的な計算グラフを利用することが特徴です。

代表機能としては、以下のものが挙げられます。

  • 「backward」:勾配計算
  • 「nn.Module」:ネットワークのクラス定義
  • 「transforms」:画像データの事前処理
  • 「make_grid」:画像表示時のグリッド表示


TensorFlowとは

TensorFlowもPytorchと同様に、深層学習によく用いられるライブラリの一種です。

多次元配列を意味する「Tensor(テンソル)」と、流れを意味する「Flow(フロー)」から「TensorFlow(テンソルフロー)」と呼ばれています。

Pytorchでは動的な計算グラフを利用するのに対して、TensorFlowでは静的な計算グラフを利用します。


PytorchとTensorFlowの違い

PytorchもTensorFlowも機械学習ライブラリである点は同じですが、前述したように計算グラフが異なります。

動的な計算グラフと静的な計算グラフには、それぞれ以下のような特徴があります。


動的な計算グラフの特徴

動的な計算グラフでは、最終的な計算グラフを構築する際に、その都度必要な計算グラフを構築して実行されます。

メリットとしては、柔軟にネットワークを構築できることや実装する際のモデル定義が容易なことが挙げられます。


静的な計算グラフの特徴

静的な計算グラフでは、最終的な計算グラフを構築するために、事前にすべての計算グラフ構築して実行されます。

メリットとしては、学習速度を高速に保つことができることや内容の理解が容易なことが挙げられます。


Python環境の準備

Pytorchをインストールする前に、事前にPythonをインストールしておきましょう。

インストールが完了したら、「python –version」でバージョンの確認をしておきます。

Pythonのバージョンが最新でない場合は、最新バージョンにアップデートしておきましょう。

アナコンダ経由でPythonをインストールした人は「conda install python=X.X(最新バージョンの値)」でアップデートが可能です。

それ以外の方は、Pythonの公式ホームページから最新のバージョンをインストールしてください。


Pytorchのインストール

事前準備が完了したところで、Pytorchのインストール方法について紹介します。

Pytorchのインストールは、下記のURLから公式サイトへ遷移することでおこなえます。

https://pytorch.org/get-started/locally/

URLへ遷移した後、画面の中心に表示されている「START LOCALLY(日本語に翻訳した場合は、ローカル環境で始める)」を確認しましょう。

ビルドバージョンやOSなど、任意の設定が選択されていると思います。

現在暫定で選択されている設定を、自身の扱っている環境に合うよう設定し直してください。

使用しているOSがWindowsならWindows、MacならMacを選択します。

選択後は「Run this Command:(次のコマンドを実行します)」を確認しましょう。

この場所には、設定した通りにインストールを実行するためのコマンドが表示されています。

(Stable・Windows・pip・Python・10.2に設定した場合は、以下の通り表示されていれば正しいです。

「pip install torch === 1.5.0 torchvision === 0.6.0 -f https://download.pytorch.org/whl/torch_stable.html」

表示されたコマンドは、コマンドプロンプト(Macの場合はターミナル)に入力して使用します。

インストールしたあとは正しくインストール出来たか確認するため、以下のコマンドを実行しましょう。

  1. 「import torch」
  2. 「print(torch.__version__)」

コマンドを実行して、Pytorchのバージョン情報が表示されればインストールは正常に完了しています。


Pytorchチュートリアル

インストールが完了したので、実際にPytorchを扱ってみましょう。

Pytorchは複数の要素で構成されており、代表的な構成は以下のものになります。

  • 「torch」:Tensorや数学関数
  • 「torch.autograd」:自動微分関数
  • 「torch.nn」:ネットワーク構築のためのデータ構造やレイヤーの定義


Pytorchのデータ構造

PytorchはTensorというデータ構造を基本としています。

Tensorはデータ型ごとに定義されているものです。

GPU上で計算する際は「torch.cuda.FloatTensor」を使用するなど環境に応じて使い分ける必要があります。

Tensorは関数「torch.tensor」を利用することで作成が可能なので、実際に作成してみましょう。


Tensorの作成

Tensorを作成する方法は多く存在するため、何種類かを取り上げて紹介します。

いずれの場合も事前に以下のコマンドを実行しておく必要があります。

  1. 「import numpy as np」
  2. 「import torch」

一番簡単な方法は、任意のリストを渡してあげる方法です。

  1. 「sumple1 = torch.tensor([[1, 2], [3, 4.]])」

1次元配列のTensorを作成する場合は以下のコマンドで作成します。

  1. 「sumple2 = torch.arange(0, 10)」

正規乱数(今回は100×10)を含んだTensorは「randa」を用いて作成します。

  1. 「sumple3 = torch.randn(100, 10)」


Tensorの変換

作成したTensorはNumpyで用いられるndarrayへ変換することが可能です。

変換をおこなう際は、以下のコマンドを実施します。

  1. 「import torch」
  2. 「import numpy as np」
  3. 「sumple1 = torch.tensor([[1, 2], [3, 4.]])」
  4. 「x = t.numpy()」
  5. 「sumple1 = torch.tensor([[1, 2], [3, 4.]], device=”cuda:0″)」
  6. 「x = sumple1.to(“cpu”).numpy()」

なお、GPU上のTensorは一度CPU上に移さないと変換することが出来ないため移動させてから変換をおこなっています。


インデクシング操作(初級)

インデックス(配列)を指定し、任意の配列から値を取り出したり変更する操作をインデクシング操作と呼びます。

  1. 「import torch」
  2. 「import numpy as np」
  3. 「sumple4 = torch.tensor([[1, 2, 3], [4, 5, 6.]])」

上記のインデックスに対して、以下のようなインデクシング操作をおこないましょう。

  • スカラーの添字を指定:「sumple4[0, 2]」
  • 添字のリストを指定:「sumple4[:,[0, 2]]」
  • マスク配列を使用して4より大きい値のみを選択:「sumple4[0 > 2]」
  • マスク配列を使用して特定の条件要素のみ置換:「sumple4[sumple4 > 2] = 100」
  • 配列の値を100に変更:「sumple4[0, 2]= 100」
  • スライスを使用して一括で値を代入:「sumple4[:, 2]= 100」

なお、マスク配列とは元の配列に対して各要素が「True・False」となっている配列のことを呼びます。

  1. 「sumple5 = [1,2,3]」

このような配列に対して、「a > 2」という操作をおこなうと

  1. 「False, False, True」

上記のようなマスク配列が作成可能です。
「a > 2」の条件を満たした値は「True」、満たさない値は「False」と返ってきています。


インデクシング操作(発展)

先ほどのインデクシング操作を実践向きにおこなうと、2種類のTensorを統合する操作が可能です。

  1. 「sumple1 = torch.tensor([[1, 2], [3, 4.]])」
  2. 「sumple6 = torch.tensor([[11, 12, 13.], [14, 15, 16.]])」
  3. 「sumple1.view(4, 1)」
  4. 「sumple1.view(1, -1)」
  5. 「sumple2.t()」
  6. 「torch.cat([sumple1, sumple6], dim = 1」

一度、それぞれの配列の形を変更させてから1つのTensorに統合させています。


チュートリアルのまとめ

チュートリアルの最後に、Pytorchのインストールからインデクシング操作までの流れをまとめて記載します。

はじめて触るという方は、上記のチュートリアルを読んだ後にコマンド操作を真似してみてください。

  1. 「import torch」
  2. 「torch.__version__」
  3. 「’x.x.x’:応答結果」
  4. 「x = torch.tensor([1, 2, 3])」
  5. 「x」
  6. 「tensor([1, 2, 3]):応答結果」
  7. 「y = x + 1」
  8. 「y」
  9. 「tensor([2, 3, 4]):応答結果」
  10. 「z = x * 3」
  11. 「z」
  12. 「tensor([3, 6, 9]):応答結果」
  13. 「y + z」
  14. 「tensor([ 5, 9, 13]):応答結果」
  15. 「a = [[[1, 2, 3], [4, 5, 6]],[[7, 8, 9], [10, 11, 12]]]」
  16. 「torch.tensor(a)」
  17. 「tensor([[[ 1, 2, 3],
  18. [ 4, 5, 6]],
  19. [[ 7, 8, 9],
  20. [10, 11, 12]]]):応答結果」
  21. 「z.numpy()」
  22. 「array([3, 6, 9]):応答結果」
  23. 「torch.randn(2, 3, 4)」
  24. 「tensor([[[ 1.2625, 0.6356, -3.0832, -1.1534],
  25. [-0.9279, 1.2728, -0.1481, 0.1624],
  26. [-1.2703, -1.4894, -1.3540, -2.0921]],
  27. [[ 0.7122, -0.0086, -1.3593, 1.3659],
  28. [-0.5189, -2.1019, -0.1081, 0.9969],
  29. [ 0.3539, -2.2619, 0.6708, -1.3612]]]):応答結果」
  30. 「torch.eye(5, 5)」
  31. 「tensor([[1., 0., 0., 0., 0.],
  32. [0., 1., 0., 0., 0.],
  33. [0., 0., 1., 0., 0.],
  34. [0., 0., 0., 1., 0.],
  35. [0., 0., 0., 0., 1.]]):応答結果」

なお下記の部分は値がランダムのため、実行した際は別の値が表示されます。

  1. 「tensor([[[ 1.2625, 0.6356, -3.0832, -1.1534],
  2. [-0.9279, 1.2728, -0.1481, 0.1624],
  3. [-1.2703, -1.4894, -1.3540, -2.0921]],
  4. [[ 0.7122, -0.0086, -1.3593, 1.3659],
  5. [-0.5189, -2.1019, -0.1081, 0.9969],
  6. [ 0.3539, -2.2619, 0.6708, -1.3612]]])」


自動微分

Tensorでは、自動微分(Autograd)をおこなうことが可能です。

定義したグラフ(計算式)に対し、事前に保持していた勾配情報を使用して計算をおこないます。

ここでは事前設定から、実際に計算をおこなう過程を紹介していきましょう。


Tensorの定義

自動微分をおこなう前に、Tensorの定義が必要です。
定義は以下の手順でおこないます。

  1. 「import torch」
  2. 「definition = torch.ones(2, 2, requires_grad=True)」
  3. 「print(sumple7)」

出力結果は以下のように表示されます。

  1. 「tensor([[1., 1.],
  2. [1., 1.]], requires_grad=True)」

無事に定義することが出来ました。


計算グラフの作成

Tensorの定義が完了したら、実際に計算をおこなうグラフを作成します。

  1. 「graph1 = definition + 2」
  2. 「print(graph1)」

作成したグラフは以下の様に出力されます。

  1. 「tensor([[3., 3.],
  2. [3., 3.]], grad_fn=)」

ここで表示されている「grad_fn」は、計算グラフが正しく作成されている証です。

先ほど作成したグラフとは別のグラフを作成します。

  1. 「graph2 = graph1 * graph1 * 3」
  2. 「consequence = graph2.mean()」
  3. 「print(graph2, consequence)」

作成したグラフは以下の様に出力されます。

  1. 「tensor([[27., 27.],
  2. [27., 27.]], grad_fn=) tensor(27., grad_fn=)」

これで事前準備は完了です。


自動微分をおこなう

Tensorの定義、計算グラフの作成が完了したので自動微分をおこないます。

はじめに解説した様に、自動微分の際には勾配が必要なため勾配を計算しておきましょう。

  1. 「consequence.backward()」

まずは勾配を求めます。

consequence(2番目に作成したグラフ)のgraph1(最初に作成したグラフ)による偏微分(d(consequence)/dx)を出力しましょう。

  1. 「print(graph1.grad)」

上記のコマンドを入力すると以下の結果が出力されます。

  1. 「tensor([[4.5000, 4.5000],
  2. [4.5000, 4.5000]])」

この値が正しいか確認するため、手動で偏微分をおこないましょう。

  1. 「consequence = graph2.mean()」
  2. 「graph2 = graph1 * graph1 * 3」

上記2つの式は以下の様に表すことが出来ます。

  1. 「consequence =1/4 * Σ(i=1,4) * graph2(i)」
  2. 「graph2(i) = 3(x(i) + 2)^2」
  3. 「graph2(i)|x(i)=1 = 27」

自動微分の時と同様に、consequenceをgraph1で偏微分します。

  1. 「∂ * consequence / ∂ * graph(i)」
  2. 「3/2(x(i) + 2)」
  3. 「9/2」
  4. 「4.5」

自動微分で求めた値(4.5000)と値が等しいため、自動微分が正しくおこなわれていることを証明できました。


自動微分で求めたグラフを描画する

自動微分が出来たので、今まで学んだ要素と組み合わせて関数のグラフを描画しましょう。

ここでは最終目標として「y = x^3 + 3x^2 + 6x + 5」のグラフを描画してみます。


コマンドの流れを理解する

まずは実際にグラフが描画できるまでのコマンドを記載します。

  1. 「import torch」
  2. 「import numpy as np」
  3. 「import matplotlib.pyplot as plt」
  4. 「x = torch.arange(-4.0, 4.0, 0.001, requires_grad=True)」
  5. 「y = calc(x)」
  6. 「y.backward(gradient=torch.ones_like(y))」
  7. 「g = x.grad」
  8. 「x_numpy = x.detach().numpy()」
  9. 「g_numpy = g.detach().numpy()」
  10. 「plt.plot(x_numpy, g_numpy, label = “Derivative”)」
  11. 「plt.xlim(-4, 3)」
  12. 「plt.ylim(-1.0, 50.0)」
  13. 「plt.grid()」
  14. 「plt.legend()」
  15. 「plt.show()」

解説抜きでコマンドを記載しましたが、流れを理解するためにこのコマンドを前半部分と後半部分に分けてみましょう。

その場合、前半部分は以下のようになります。

  1. 「import torch」
  2. 「import numpy as np」
  3. 「import matplotlib.pyplot as plt」
  4. 「x = torch.arange(-4.0, 4.0, 0.001, requires_grad=True)」
  5. 「y = calc(x)」
  6. 「y.backward(gradient=torch.ones_like(y))」
  7. 「g = x.grad」

すると、前半部分は今までに学習した要素を組み合わせることで実施可能なはずです。

「x = torch.arange(-4.0, 4.0, 0.001, requires_grad=True)」で勾配計算の設定をおこない、「y = calc(x)」により計算グラフを構築します。

次に「y.backward(gradient=torch.ones_like(y))」で勾配を利用した自動微分がおこなわれています。

流れをまとめると複雑そうに見えても、順を追うと理解出来る内容であることも多いです。

ここまでの内容が難しい場合は再度、Tensorの作成や定義・インデクシング操作などを復習してみてください。


可視化しやすいグラフを描画

前半部分は今までの知識でコマンドを書くことが出来たので、後半部分を解説します。

グラフを描画するには、下記の様な操作が必要です。

  1. 「x_numpy = x.detach().numpy()」
  2. 「g_numpy = g.detach().numpy()」
  3. 「plt.plot(x_numpy, g_numpy, label = “Derivative”)」
  4. 「plt.xlim(-4, 3)」
  5. 「plt.ylim(-1.0, 50.0)」
  6. 「plt.grid()」
  7. 「plt.legend()」
  8. 「plt.show()」

上記のコマンドでグラフを描画する際は事前にデタッチという操作が必要になります。

  1. 「x_numpy = x.detach().numpy()」
  2. 「g_numpy = g.detach().numpy()」

デタッチ(detach)とは「分離する」という意味がありTensorに追跡されないためにおこないます。

Tensorでは常に計算履歴が追跡されており、デタッチをしないと過去の計算結果が反映されてしまうためです。

  1. 「plt.plot(x_numpy, g_numpy, label = “Derivative”)」
  2. 「plt.xlim(-5, 5)」
  3. 「plt.ylim(-1.0, 50.0)」

この3つのコマンドでx軸、y軸それぞれの最小値~最大値を指定しています。

描画されるグラフのx軸(横)、y軸(縦)に当たる部分です。

この部分を任意の値に変化させることで、グラフの構造が変化します。

  1. 「plt.grid()」
  2. 「plt.legend()」

この2つのコマンドでグリッドを表示させ、グラフの線の解説(グラフ内右上部分)を表示させることが可能です。

これをおこなうことで、何もしない時と比べてグラフの可視化がしやすくなります。

ここまでの操作で見やすいグラフの描画が完了したので「plt.show()」によりグラフを表示させています。

実務では、ただグラフを描画するだけでなく見やすさも求められるため、あえて一手間を加えました。

出来るならコマンドを丸写しするのでは無く、自分自身でコマンドの値を変更し、グラフがどう変化するか確認してみてください。


まとめ

内容はいかがだったでしょうか。

今回は深層学習を初めておこなう方を対象に、Pytorchの基本的な使い方について説明しました。

Pytorchでは今回学んだTensorや自動微分などの機能以外にも、ネットワークの構築など多彩な機能があります。

どれも一朝一夕で身に付く内容ではないですが、確実にマスターし更なるステップアップを目指していきましょう。


toiroフリーランスは、SHIFTグループがプライムとして参画している独自案件をフリーランスエンジニア向けに紹介する唯一のプラットフォームサービスです。

エージェントによるサポートもありますので、ご利用を検討してみてはいかがでしょうか。