ComposeTransformを使ったさまざまな変換のサンプルコード

2024-04-02

PyTorchの確率分布におけるComposeTransformの詳細解説

torch.distributions.transforms.ComposeTransform は、複数の変換を組み合わせて、確率分布を操作するための便利なクラスです。 データの標準化や正規化、スケーリングなど、さまざまな前処理を簡単に実行できます。

ComposeTransformの利点

  • 複数の変換を単一の変換として扱える
  • コードの簡潔化と可読性の向上
  • 複雑な変換パイプラインを容易に構築できる

ComposeTransformの使い方

使用例

from torch.distributions import transforms
from torch.distributions import normal

# 標準正規分布
base_distribution = normal.Normal(torch.tensor(0.), torch.tensor(1.))

# スケーリング変換とシフト変換を定義
scale_transform = transforms.Scale(torch.tensor(2.))
shift_transform = transforms.Shift(torch.tensor(3.))

# ComposeTransformを使って変換を組み合わせる
composed_transform = transforms.ComposeTransform([scale_transform, shift_transform])

# 変換後の分布
transformed_distribution = base_distribution.transform(composed_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

ComposeTransformの引数

  • transforms: 適用する変換のリスト

ComposeTransformのメソッド

  • __call__(self, x): 変換を適用
  • inverse(self, x): 逆変換を適用
  • log_abs_det_jacobian(self, x, y): ヤコビアンの絶対値の対数

ComposeTransformの注意点

  • 変換の順序は重要です。
  • 逆変換が存在しない変換もあります。

補足

  • 本解説は、PyTorch 1.10.1 をベースにしています。
  • 他の確率分布ライブラリでも、同様の機能を提供している場合があります。


ComposeTransformを使ったさまざまな変換のサンプルコード

データの標準化と正規化

from torch.distributions import transforms
from torch.distributions import normal

# データ
data = torch.tensor([1., 2., 3., 4., 5.])

# 標準化
standardize_transform = transforms.Standardize(data.mean(), data.std())
standardized_data = standardize_transform(data)

# 正規化
normalize_transform = transforms.Normalize((0., 1.))
normalized_data = normalize_transform(standardized_data)

print(normalized_data)

スケーリングとシフト

from torch.distributions import transforms
from torch.distributions import uniform

# 区間[0, 1]の一様分布
base_distribution = uniform.Uniform(torch.tensor(0.), torch.tensor(1.))

# スケーリング変換とシフト変換
scale_transform = transforms.Scale(torch.tensor(10.))
shift_transform = transforms.Shift(torch.tensor(5.))

# ComposeTransformを使って変換を組み合わせる
composed_transform = transforms.ComposeTransform([scale_transform, shift_transform])

# 変換後の分布
transformed_distribution = base_distribution.transform(composed_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

ロジット変換と逆ロジット変換

from torch.distributions import transforms
from torch.distributions import bernoulli

# ベルヌーイ分布
base_distribution = bernoulli.Bernoulli(torch.tensor(0.5))

# ロジット変換
logit_transform = transforms.Logit()
logit_data = logit_transform(base_distribution.probs)

# 逆ロジット変換
inverse_logit_transform = transforms.InverseLogit()
probs = inverse_logit_transform(logit_data)

print(probs)

順序統計量変換

from torch.distributions import transforms
from torch.distributions import exponential

# 指数分布
base_distribution = exponential.Exponential(torch.tensor(1.))

# 順序統計量変換
order_statistic_transform = transforms.OrderStatistic(0.9)

# 変換後の分布
transformed_distribution = base_distribution.transform(order_statistic_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

ランダム変換

from torch.distributions import transforms
from torch.distributions import normal

# 標準正規分布
base_distribution = normal.Normal(torch.tensor(0.), torch.tensor(1.))

# ランダムなスケーリング変換
random_scale_transform = transforms.RandomScale(torch.tensor(0.5), torch.tensor(1.5))

# ランダムなシフト変換
random_shift_transform = transforms.RandomShift(torch.tensor(-1.), torch.tensor(1.))

# ランダム変換のリスト
random_transforms = [random_scale_transform, random_shift_transform]

# ランダムな変換を適用
composed_transform = transforms.ComposeTransform(random_transforms)

# 変換後の分布
transformed_distribution = base_distribution.transform(composed_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

条件付き変換

from torch.distributions import transforms
from torch.distributions import normal

# 条件付き正規分布
base_distribution = normal.Normal(torch.tensor(0.), torch.tensor(1.))

# 条件付きスケーリング変換
conditional_scale_transform = transforms.ConditionalScale(lambda x: x * 2.)

# 条件付きシフト変換
conditional_shift_transform = transforms.ConditionalShift(lambda x: x + 1.)

# 条件付き変換のリスト
conditional_transforms = [conditional_scale_transform, conditional_shift_transform]

# 条件付き変換を適用
composed_transform = transforms.ComposeTransform(conditional_transforms)

# 変換後の分布
transformed_distribution = base_distribution.transform(composed_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample(condition=torch.tensor(1.))

print(sample)


ComposeTransform 以外の確率分布変換方法

TransformedDistribution クラス

torch.distributions.TransformedDistribution クラスは、既存の分布に任意の変換を適用するための汎用的な方法を提供します。

from torch.distributions import transforms
from torch.distributions import normal

# 標準正規分布
base_distribution = normal.Normal(torch.tensor(0.), torch.tensor(1.))

# スケーリング変換
scale_transform = transforms.Scale(torch.tensor(2.))

# TransformedDistribution を使って変換を適用
transformed_distribution = TransformedDistribution(base_distribution, scale_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

自作の変換クラス

特定の変換を繰り返し使用する場合は、自作の変換クラスを作成すると便利です。

from torch.distributions import transforms

class MyTransform(transforms.Transform):
    def __init__(self, scale):
        self.scale = scale

    def __call__(self, x):
        return x * self.scale

    def inverse(self, x):
        return x / self.scale

    def log_abs_det_jacobian(self, x, y):
        return torch.log(self.scale)

# 標準正規分布
base_distribution = normal.Normal(torch.tensor(0.), torch.tensor(1.))

# 自作の変換クラス
my_transform = MyTransform(torch.tensor(2.))

# TransformedDistribution を使って変換を適用
transformed_distribution = TransformedDistribution(base_distribution, my_transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

ラムダ式

簡単な変換の場合は、ラムダ式を使って変換を定義することができます。

from torch.distributions import transforms
from torch.distributions import uniform

# 区間[0, 1]の一様分布
base_distribution = uniform.Uniform(torch.tensor(0.), torch.tensor(1.))

# ラムダ式を使って変換を定義
transform = lambda x: x ** 2

# TransformedDistribution を使って変換を適用
transformed_distribution = TransformedDistribution(base_distribution, transform)

# 変換後の分布からサンプルを取得
sample = transformed_distribution.sample()

print(sample)

その他のライブラリ

PyroTensorFlow Probability などのライブラリは、PyTorch 標準の torch.distributions モジュールよりも多くの変換を提供しています。

  • 簡単な変換の場合は、ラムダ式を使うのが最も簡単です。
  • 複数の変換を組み合わせて適用する場合は、ComposeTransform または TransformedDistribution クラスを使うのが便利です。
  • 特定の変換を繰り返し使用する場合は、自作の変換クラスを作成するとコードを整理できます。
  • PyTorch 標準の torch.distributions モジュールにない変換が必要場合は、他のライブラリを検討する必要があります。