GPyTorch 1.2 Examples : 基本使用方法 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 09/04/2020 (1.2)
* 本ページは、GPyTorch 1.2 ドキュメントの以下のページを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
基本使用方法
このフォルダはパッケージの基本的な使用方法のためのノートブックを含みます、e.g. ハイパーパラメータ、パラメータ制約と事前分布の処理、そしてモデルのセーブとロードのようなものです。
これらを調べる前に、GPyTorch モデルの構造 (= anatomy) を詳述する 単純な GP 回帰チュートリアル を調べることを望むかもしれません。
- raw vs. actual パラメータ、制約、事前分布等のようなことの情報のためには ハイパーパラメータ上のチュートリアル を調べてください。
- モデルをセーブしてロードするノートブックは GPyTorch モデルをどのようにディスク上にセーブしてロードするかを詳述します。
基本使用方法 – GPyTorch のハイパーパラメータ
このノートブックの目的は GPyTorch の GP ハイパーパラメータがどのように動作し、それらがどのように処理され、制約と事前分布のためにどのようなオプションが利用可能であるか、そして物事が他のパッケージとどのように異なるかもしれないのかを説明することです。
Note: これは GPyTorch のハイパーパラメータへの基本的なイントロダクションです。Pyro 分布のようなものと共に GPyTorch ハイパーパラメータを使用することを望む場合、それは「基本使用方法」チュートリアルではあまりカバーされません。
import math import torch import gpytorch from matplotlib import pyplot as plt from IPython.display import Markdown, display def printmd(string): display(Markdown(string))
サンプルモデルを定義する
次のセルで、単純な GP 回帰チュートリアルからの単純な exact GP を定義します。このモデルを使用してハイパーパラメータ作成の特定の様相を実演します。
train_x = torch.linspace(0, 1, 100) train_y = torch.sin(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * 0.2 # We will use the simplest form of GP model, exact inference class ExactGPModel(gpytorch.models.ExactGP): def __init__(self, train_x, train_y, likelihood): super(ExactGPModel, self).__init__(train_x, train_y, likelihood) self.mean_module = gpytorch.means.ConstantMean() self.covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel()) def forward(self, x): mean_x = self.mean_module(x) covar_x = self.covar_module(x) return gpytorch.distributions.MultivariateNormal(mean_x, covar_x) # initialize likelihood and model likelihood = gpytorch.likelihoods.GaussianLikelihood() model = ExactGPModel(train_x, train_y, likelihood)
モデル・ハイパーパラメータを見る
モデル・パラメータを見てみましょう。「パラメータ」によって、ここでは autograd により満たされた勾配を持つ、タイプ torch.nn.Parameter のオブジェクトを明示的に意味しています。これらにアクセスするには、 torch でこれを行なう 2 つの方法があります。一つの方法は model.state_dict() を使用することです、ここではモデルをセーブするためにこの利用方法を実演します。
次のセルでは model.named_parameters() generator に渡りループすることによりこれを行なうもう一つの方法を実演します :
for param_name, param in model.named_parameters(): print(f'Parameter name: {param_name:42} value = {param.item()}')
Parameter name: likelihood.noise_covar.raw_noise value = 0.0 Parameter name: mean_module.constant value = 0.0 Parameter name: covar_module.raw_outputscale value = 0.0 Parameter name: covar_module.base_kernel.raw_lengthscale value = 0.0
Raw vs Actual パラメータ
ここで注意すべき最も重要なことは、モデルの実際に学習されたパラメータは raw_noise, raw_outputscale, raw_lengthscale 等のようなものであることです。この理由はこれらのパラメータは正 (= positive) でなければならないためです。これはパラメータのための次のトピックをもたらします : 制約、そして raw パラメータと actual パラメータ間の相違です。
ハイパーパラメータのために正値性と他の制約を強要するために、GPyTorch は raw パラメータ (e.g., model.covar_module.raw_outputscale) を持ちます、これはある制約を通して actual 値に変換されます。raw outputscale、その制約、そして最終値を見ましょう :
raw_outputscale = model.covar_module.raw_outputscale print('raw_outputscale, ', raw_outputscale) # Three ways of accessing the raw outputscale constraint print('\nraw_outputscale_constraint1', model.covar_module.raw_outputscale_constraint) printmd('\n\n**Printing all model constraints...**\n') for constraint_name, constraint in model.named_constraints(): print(f'Constraint name: {constraint_name:55} constraint = {constraint}') printmd('\n**Getting raw outputscale constraint from model...**') print(model.constraint_for_parameter_name("covar_module.raw_outputscale")) printmd('\n**Getting raw outputscale constraint from model.covar_module...**') print(model.covar_module.constraint_for_parameter_name("raw_outputscale"))
raw_outputscale, Parameter containing: tensor(0., requires_grad=True) raw_outputscale_constraint1 Positive() Printing all model constraints… Constraint name: likelihood.noise_covar.raw_noise_constraint constraint = GreaterThan(1.000E-04) Constraint name: covar_module.raw_outputscale_constraint constraint = Positive() Constraint name: covar_module.base_kernel.raw_lengthscale_constraint constraint = Positive() Getting raw outputscale constraint from model… Positive() Getting raw outputscale constraint from model.covar_module… Positive()
制約はどのように動作するのでしょう?
制約は transform と inverse_transform メソッドを定義します、これらは raw パラメータをリアルなものに変えます。正の制約については、変換された値が常に正であることを想定します。見ましょう :
raw_outputscale = model.covar_module.raw_outputscale constraint = model.covar_module.raw_outputscale_constraint print('Transformed outputscale', constraint.transform(raw_outputscale)) print(constraint.inverse_transform(constraint.transform(raw_outputscale))) print(torch.equal(constraint.inverse_transform(constraint.transform(raw_outputscale)), raw_outputscale)) print('Transform a bunch of negative tensors: ', constraint.transform(torch.tensor([-1., -2., -3.])))
Transformed outputscale tensor(0.6931, grad_fn=) tensor(0., grad_fn= ) True Transform a bunch of negative tensors: tensor([0.3133, 0.1269, 0.0486])
変換された値のための便利な Getter/Setter
raw パラメータ値を扱うのは煩わしいので (e.g., 私達は 0.01 のノイズ分散が何を意味するか知っているかもしれませんが、-2.791 の raw_noise については多分そうではないでしょう)、実際上は raw パラメータを定義する総ての組込み GPyTorch モジュールは変換された値を直接扱うために便利な getter と setter を定義します。
次のセルでは、outputscale を得て設定する「不便な方法」と「便利な」方法を実演します。
# Recreate model to reset outputscale model = ExactGPModel(train_x, train_y, likelihood) # Inconvenient way of getting true outputscale raw_outputscale = model.covar_module.raw_outputscale constraint = model.covar_module.raw_outputscale_constraint outputscale = constraint.transform(raw_outputscale) print(f'Actual outputscale: {outputscale.item()}') # Inconvenient way of setting true outputscale model.covar_module.raw_outputscale.data.fill_(constraint.inverse_transform(torch.tensor(2.))) raw_outputscale = model.covar_module.raw_outputscale outputscale = constraint.transform(raw_outputscale) print(f'Actual outputscale after setting: {outputscale.item()}')
Actual outputscale: 0.6931471824645996 Actual outputscale after setting: 2.0
Ouch, that is ugly! 幸い、より良い方法があります :
# Recreate model to reset outputscale model = ExactGPModel(train_x, train_y, likelihood) # Convenient way of getting true outputscale print(f'Actual outputscale: {model.covar_module.outputscale}') # Convenient way of setting true outputscale model.covar_module.outputscale = 2. print(f'Actual outputscale after setting: {model.covar_module.outputscale}')
Actual outputscale: 0.6931471824645996 Actual outputscale after setting: 2.0
パラメータ制約を変更する
モデルの実際のノイズを見れば、GPyTorch はノイズ分散のために 1e-4 のデフォルト下界を定義します :
print(f'Actual noise value: {likelihood.noise}') print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}')
Actual noise value: tensor([0.6932], grad_fn=<AddBackward0>) Noise constraint: GreaterThan(1.000E-04)
ノイズ制約を on the fly か likelihood が作成されたときのいずれかで変更できます :
likelihood = gpytorch.likelihoods.GaussianLikelihood(noise_constraint=gpytorch.constraints.GreaterThan(1e-3)) print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}') ## Changing the constraint after the module has been created likelihood.noise_covar.register_constraint("raw_noise", gpytorch.constraints.Positive()) print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}')
Noise constraint: GreaterThan(1.000E-03) Noise constraint: Positive()
事前分布
GPyTorch では、事前分布は貴方がモデルに登録する、任意のパラメータの任意の恣意的な関数上で作用するものです。制約のように、これらは通常は (カーネルや尤度のような) オブジェクトを作成するときか、後で on the fly に設定するときに定義できます。
ここに幾つかの例があります :
# Registers a prior on the sqrt of the noise parameter # (e.g., a prior for the noise standard deviation instead of variance) likelihood.noise_covar.register_prior( "noise_std_prior", gpytorch.priors.NormalPrior(0, 1), lambda: likelihood.noise.sqrt() ) # Create a GaussianLikelihood with a normal prior for the noise likelihood = gpytorch.likelihoods.GaussianLikelihood( noise_constraint=gpytorch.constraints.GreaterThan(1e-3), noise_prior=gpytorch.priors.NormalPrior(0, 1) )
一つに上手くまとめる
次のセルでは、モデルを作成するときにハイパーパラメータとより厳しい制約に渡る幾つかの事前分布を置くために ExactGP 定義を増強します。
# We will use the simplest form of GP model, exact inference class FancyGPWithPriors(gpytorch.models.ExactGP): def __init__(self, train_x, train_y, likelihood): super(FancyGPWithPriors, self).__init__(train_x, train_y, likelihood) self.mean_module = gpytorch.means.ConstantMean() lengthscale_prior = gpytorch.priors.GammaPrior(3.0, 6.0) outputscale_prior = gpytorch.priors.GammaPrior(2.0, 0.15) self.covar_module = gpytorch.kernels.ScaleKernel( gpytorch.kernels.RBFKernel( lengthscale_prior=lengthscale_prior, ), outputscale_prior=outputscale_prior ) # Initialize lengthscale and outputscale to mean of priors self.covar_module.base_kernel.lengthscale = lengthscale_prior.mean self.covar_module.outputscale = outputscale_prior.mean def forward(self, x): mean_x = self.mean_module(x) covar_x = self.covar_module(x) return gpytorch.distributions.MultivariateNormal(mean_x, covar_x) likelihood = gpytorch.likelihoods.GaussianLikelihood( noise_constraint=gpytorch.constraints.GreaterThan(1e-2), ) model = FancyGPWithPriors(train_x, train_y, likelihood)
一つの呼び出しでハイパーパラメータを初期化する
便利のために、GPyTorch モジュールはまた initialize メソッドも定義します、これはサブモジュール上のパラメータの完全な辞書を更新することを可能にします。例えば :
hypers = { 'likelihood.noise_covar.noise': torch.tensor(1.), 'covar_module.base_kernel.lengthscale': torch.tensor(0.5), 'covar_module.outputscale': torch.tensor(2.), } model.initialize(**hypers) print( model.likelihood.noise_covar.noise.item(), model.covar_module.base_kernel.lengthscale.item(), model.covar_module.outputscale.item() )
1.0000001192092896 0.4999999701976776 2.0
基本使用方法 – モデルをセーブしてロードする
この一口サイズのノートブックでは、モデルをどのようにセーブしてロードするかを調べます。一般に、その過程は任意の PyTorch モジュールのためと同じです。
import math import torch import gpytorch from matplotlib import pyplot as plt
単純なモデルをセーブする
最初に、セーブしたい GP モデルを定義します。下で使用されるモデルは単純な GP 回帰チュートリアルからのモデルと同じです。
train_x = torch.linspace(0, 1, 100) train_y = torch.sin(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * 0.2
# We will use the simplest form of GP model, exact inference class ExactGPModel(gpytorch.models.ExactGP): def __init__(self, train_x, train_y, likelihood): super(ExactGPModel, self).__init__(train_x, train_y, likelihood) self.mean_module = gpytorch.means.ConstantMean() self.covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel()) def forward(self, x): mean_x = self.mean_module(x) covar_x = self.covar_module(x) return gpytorch.distributions.MultivariateNormal(mean_x, covar_x) # initialize likelihood and model likelihood = gpytorch.likelihoods.GaussianLikelihood() model = ExactGPModel(train_x, train_y, likelihood)
モデル状態を変更する
モデル・セービングを実演するため、下でデフォルト値からハイパーパラメータを変更します。ここで何が起きているかのより多くの情報については、ハイパーパラメータの初期化のチュートリアル・ノートブックを見てください。
model.covar_module.outputscale = 1.2 model.covar_module.base_kernel.lengthscale = 2.2
モデル状態を得る
GPyTorch モデルの完全な状態を得るには、単純に任意の PyTorch モデル上でするように state_dict を呼び出します。state dict は raw パラメータ値を含むことに注意してください。これは、これらは GPyTorch で学習された実際の torch.nn.Parameters であるからです。これについてのより多くの情報は再度、ハイパーパラメータのノートブックを見てください。
model.state_dict()
OrderedDict([('likelihood.noise_covar.raw_noise', tensor([0.])), ('mean_module.constant', tensor([0.])), ('covar_module.raw_outputscale', tensor(0.8416)), ('covar_module.base_kernel.raw_lengthscale', tensor([[2.0826]]))])
モデル状態をセーブする
上の状態辞書はモデルのための総ての訓練可能なパラメータを表します。従って、これを次のようにしてファイルにセーブできます :
torch.save(model.state_dict(), 'model_state.pth')
モデル状態をロードする
次に、この状態を新しいモデルにロードしてそしてパラメータが正しく更新されたことを示します。
state_dict = torch.load('model_state.pth') model = ExactGPModel(train_x, train_y, likelihood) # Create a new GP model model.load_state_dict(state_dict)
IncompatibleKeys(missing_keys=[], unexpected_keys=[])
model.state_dict()
OrderedDict([('likelihood.noise_covar.raw_noise', tensor([0.])), ('mean_module.constant', tensor([0.])), ('covar_module.raw_outputscale', tensor(0.8416)), ('covar_module.base_kernel.raw_lengthscale', tensor([[2.0826]]))])
より複雑な例
次に、より複雑な exact GP 上で同じ原理を実演します、そこではモデルの一部として単純な順伝播ニューラルネットワーク特徴抽出器を持ちます。
class GPWithNNFeatureExtractor(gpytorch.models.ExactGP): def __init__(self, train_x, train_y, likelihood): super(GPWithNNFeatureExtractor, self).__init__(train_x, train_y, likelihood) self.mean_module = gpytorch.means.ConstantMean() self.covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel()) self.feature_extractor = torch.nn.Sequential( torch.nn.Linear(1, 2), torch.nn.BatchNorm1d(2), torch.nn.ReLU(), torch.nn.Linear(2, 2), torch.nn.BatchNorm1d(2), torch.nn.ReLU(), ) def forward(self, x): x = self.feature_extractor(x) mean_x = self.mean_module(x) covar_x = self.covar_module(x) return gpytorch.distributions.MultivariateNormal(mean_x, covar_x) # initialize likelihood and model likelihood = gpytorch.likelihoods.GaussianLikelihood() model = GPWithNNFeatureExtractor(train_x, train_y, likelihood)
モデル状態を得る
次のセルでは、model.state_dict() を通して再度モデル状態をプリントします。見れるように、状態は実質的により複雑です、何故ならばモデルは今ではニューラルネットワーク・パラメータを含むからです。それにも関わらず、セーブとロードは単純で簡単です。
model.state_dict()
OrderedDict([('likelihood.noise_covar.raw_noise', tensor([0.])), ('mean_module.constant', tensor([0.])), ('covar_module.raw_outputscale', tensor(0.)), ('covar_module.base_kernel.raw_lengthscale', tensor([[0.]])), ('feature_extractor.0.weight', tensor([[-0.9135], [-0.5942]])), ('feature_extractor.0.bias', tensor([ 0.9119, -0.0663])), ('feature_extractor.1.weight', tensor([0.2263, 0.2209])), ('feature_extractor.1.bias', tensor([0., 0.])), ('feature_extractor.1.running_mean', tensor([0., 0.])), ('feature_extractor.1.running_var', tensor([1., 1.])), ('feature_extractor.1.num_batches_tracked', tensor(0)), ('feature_extractor.3.weight', tensor([[-0.6375, -0.6466], [-0.0563, -0.4695]])), ('feature_extractor.3.bias', tensor([-0.1247, 0.0803])), ('feature_extractor.4.weight', tensor([0.0466, 0.7248])), ('feature_extractor.4.bias', tensor([0., 0.])), ('feature_extractor.4.running_mean', tensor([0., 0.])), ('feature_extractor.4.running_var', tensor([1., 1.])), ('feature_extractor.4.num_batches_tracked', tensor(0))])
torch.save(model.state_dict(), 'my_gp_with_nn_model.pth') state_dict = torch.load('my_gp_with_nn_model.pth') model = GPWithNNFeatureExtractor(train_x, train_y, likelihood) model.load_state_dict(state_dict)
IncompatibleKeys(missing_keys=[], unexpected_keys=[])
model.state_dict()
OrderedDict([('likelihood.noise_covar.raw_noise', tensor([0.])), ('mean_module.constant', tensor([0.])), ('covar_module.raw_outputscale', tensor(0.)), ('covar_module.base_kernel.raw_lengthscale', tensor([[0.]])), ('feature_extractor.0.weight', tensor([[-0.9135], [-0.5942]])), ('feature_extractor.0.bias', tensor([ 0.9119, -0.0663])), ('feature_extractor.1.weight', tensor([0.2263, 0.2209])), ('feature_extractor.1.bias', tensor([0., 0.])), ('feature_extractor.1.running_mean', tensor([0., 0.])), ('feature_extractor.1.running_var', tensor([1., 1.])), ('feature_extractor.1.num_batches_tracked', tensor(0)), ('feature_extractor.3.weight', tensor([[-0.6375, -0.6466], [-0.0563, -0.4695]])), ('feature_extractor.3.bias', tensor([-0.1247, 0.0803])), ('feature_extractor.4.weight', tensor([0.0466, 0.7248])), ('feature_extractor.4.bias', tensor([0., 0.])), ('feature_extractor.4.running_mean', tensor([0., 0.])), ('feature_extractor.4.running_var', tensor([1., 1.])), ('feature_extractor.4.num_batches_tracked', tensor(0))])
以上