PyTorchで最急降下法による最適化
余談
Differentiable Programming(微分可能プログラミング)という捉え方がとても素晴らしいと個人的に思います. 詳しくは以下のブログを見てください.
最急降下法による最適化
機械学習フレームワークのPyTorchを使って単純に最急降下法で関数の最適化をする. 最適化する関数は10000000次元のsphere関数を使用する.
コード
# -*- coding: utf-8 -*- import torch import torch.nn as nn import torch.optim as optim # 評価関数 def _sphere(x): return torch.pow(x, 2).sum() class Model(nn.Module): def __init__(self, dim=100, func=None): super(Model, self).__init__() if func is None: func = _sphere self.func = func # 設計変数 self.x = nn.Parameter(torch.rand(dim)) def forward(self): return self.func(self.x) def vars(self): return self.x.detach().numpy() def objective(self): with torch.no_grad(): return self.func(self.x).numpy() # 次元は10000000次元 model = Model(dim=10000000) optimizer = optim.SGD(model.parameters(), lr=0.1) N = 100 print('初期の評価値', model.objective()) print('optimization...') for i in range(N): output = model() optimizer.zero_grad() loss = output loss.backward() optimizer.step() print('\rloss:', loss.item(), end='') print() print('評価値', model.objective())
実行結果
初期の評価値 3331740.8 optimization... loss: 2.1601908358880734e-13 評価値 1.382506e-13
gist
参考
GPyTorchで多クラス分類
GPyTorchとは
PyTorch上で実装されたガウス過程(GP)のライブラリである. ハイパーパラメータをPyTorchのシステムを使用してGPUなどで最適化できるのが特徴である. GPは逆行列を計算する際に の計算量がかかり,スケーラビリティに難があるが,PyTorchのシステムを使ってそれを補おうとしている.
GPでは尤度を変更することで回帰や二値分類,多クラス分類が可能となる. 今回はアヤメ(iris)データセットを用いて3クラスの多クラス分類をGPyTorchで分類してみる.
コード
# -*- coding: utf-8 -*- import torch import gpytorch import numpy as np from gpytorch.models import AbstractVariationalGP from gpytorch.variational import CholeskyVariationalDistribution from gpytorch.mlls.variational_elbo import VariationalELBO from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split class GPMultiClassificationModel(AbstractVariationalGP): def __init__(self, num_dim=2, grid_bounds=(-10., 10.), grid_size=64): variational_distribution = gpytorch.variational.CholeskyVariationalDistribution( num_inducing_points=grid_size, batch_size=num_dim ) variational_strategy = gpytorch.variational.AdditiveGridInterpolationVariationalStrategy( self, grid_size=grid_size, grid_bounds=[grid_bounds], num_dim=num_dim, variational_distribution=variational_distribution, mixing_params=False, sum_output=False ) super().__init__(variational_strategy) 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) latent_pred = gpytorch.distributions.MultivariateNormal(mean_x, covar_x) return latent_pred device = 'cuda' if torch.cuda.is_available() else 'cpu' iris = load_iris() # 特徴量は2次元 # testデータは3割 (train_x, test_x, train_y, test_y) = train_test_split(iris.data[:, [0, 2]].astype(np.float32), iris.target, test_size=0.3, random_state=0,) # 3クラス分類 n_labels = len(np.unique(train_y)) train_x = torch.from_numpy(train_x).to(device) train_y = torch.from_numpy(train_y).to(device) test_x = torch.from_numpy(test_x).to(device) test_y = torch.from_numpy(test_y).to(device) model = GPMultiClassificationModel(num_dim=2).to(device) # 多クラス分類なのでSoftmaxを使用する likelihood = gpytorch.likelihoods.SoftmaxLikelihood(num_classes=n_labels, num_features=2).to(device) # 周辺尤度 mll = VariationalELBO(likelihood, model, train_y.numel()) optimizer = torch.optim.Adam(model.parameters(), lr=0.1) # train model.train() likelihood.train() training_iter = 10000 for i in range(training_iter): optimizer.zero_grad() output = model(train_x) loss = -mll(output, train_y) loss.backward() print('\rIter %d/%d - Loss: %.3f' % (i + 1, training_iter, loss.item()), end='') optimizer.step() print('') # test model.eval() likelihood.eval() correct = 0.0 # sample数を64とする with torch.no_grad(), gpytorch.settings.num_likelihood_samples(64): data = test_x target = test_y output = likelihood(model(data)) pred = output.probs.mean(0).argmax(-1) correct += pred.eq(target.view_as(pred)).cpu().sum() acc = correct.numpy() / float(len(test_x)) * 100.0 print('Accuracy:', acc)
出力結果
Iter 10000/10000 - Loss: 0.397 Accuracy: 86.66666666666667
gist
参考
カーネル密度推定とエントロピー
カーネル密度推定をしてエントロピーを計算する方法を忘備録として書いておく.
標準正規分布を例にとって計算してみる. ちなみに理論値は
import numpy as np import matplotlib.pyplot as plt from scipy import integrate from scipy.stats import norm from scipy.stats import gaussian_kde x = norm.rvs(size=10000) plt.hist(x, bins=50) plt.title('sampling') plt.show() k = gaussian_kde(x) X = np.linspace(-5, 5, 100) plt.plot(X, norm().pdf(X), label='norm') plt.plot(X, k(X), label='kernel estimation') plt.legend() plt.title('pdf') plt.show() def entropy(pdf): def f(x): px = pdf(x) return - px * np.log(px) return f entropy_k = entropy(k) print('entropy:') print('カーネル密度推定', integrate.quad(entropy_k, -5.0, 5.0)) print('理論値', np.log(np.sqrt(2*np.pi*np.e)))
出力結果
entropy: カーネル密度推定 (1.4426170314750473, 1.2370907729071283e-08) 理論値 1.4189385332046727
gist
Pykkaでping pong
Pythonでactor model使いたかったので忘備録として
Pykka
Pythonのactor model frameworkです.
インストール方法
pip install pykka
Actorの実装方法
標準のThreadによる実装やgeventなどがある.
ドキュメントに gevent
の方が一般的に Thread
よりも早いことが記載されている.
gevent実装のActorを使用する場合,geventのインストール方法
pip install gevent
通信方法
ask
は同期通信,tell
は非同期通信となっている.
ask
の場合は返り値を受け取れるが,tell
の場合は None
が返り値となる.
ping pong
pingというメッセージに対してpongを返すサンプルを書いてみる.
以下は threading.Thread
の実装
from pykka import ThreadingActor class Actor(ThreadingActor): def __init__(self): super().__init__() def on_receive(self, message): print('recieve:', message) return 'pong' actor = Actor.start() r = actor.tell('ping') # None print('tell:', r) r = actor.ask('ping') # pong print('ask:', r) actor.stop() ''' tell: None recieve: ping recieve: ping ask: pong '''
以下は gevent
の実装
from pykka.gevent import GeventActor class Gactor(GeventActor): def __init__(self): super().__init__() def on_receive(self, message): print('recieve:', message) return 'pong' gactor = Gactor.start() r = gactor.tell('ping') print('tell:', r) r = gactor.ask('ping') print('ask:', r) gactor.stop() ''' tell: None recieve: ping recieve: ping ask: pong '''
参考
混合ガウス過程で多峰な関数を回帰する
SSH in Elixir
ElixirでSSHをする方法を忘備録として書いておく.
iexして以下を実行する
iex(1)> ip = '1.2.3.4' '1.2.3.4' iex(2)> port = 22 22 iex(3)> ssh_config = [ ...(3)> user: 'user', ...(3)> password: 'password1234', ...(3)> silently_accept_hosts: true ...(3)> ] [user: 'user', password: 'password1234', silently_accept_hosts: true] iex(4)> :ssh.start :ok iex(5)> :ssh.shell(ip, port, ssh_config) remote > # login
command実行の場合
iex(1)> ip = '1.2.3.4' '1.2.3.4' iex(2)> port = 22 22 iex(3)> ssh_config = [ ...(3)> user: 'user', ...(3)> password: 'password1234', ...(3)> silently_accept_hosts: true ...(3)> ] [user: 'user', password: 'password1234', silently_accept_hosts: true] iex(4)> :ssh.start :ok iex(5)> {:ok, con_ref} = :ssh.connect(ip, port, ssh_config) {:ok, #PID<0.128.0>} iex(6)> {ok, ch_id} = :ssh_connection.session_channel(con_ref, :infinity) {:ok, 0} iex(7)> :ssh_connection.exec(con_ref, ch_id, "date", :infinity) :success iex(8)> flush {:ssh_cm, #PID<0.128.0>, {:data, 0, 0, "2019年 9月 6日 金曜日 12:36:26 JST\n"}} {:ssh_cm, #PID<0.128.0>, {:eof, 0}} {:ssh_cm, #PID<0.128.0>, {:exit_status, 0, 0}} {:ssh_cm, #PID<0.128.0>, {:closed, 0}} :ok
参考
Distributed Elixir
Elixirで分散ノードで処理をする方法を忘備録として書いておく.
環境
- node01
- 192.168.0.2
- node02
- 192.168.0.3
方法
node01で以下のプロセスを立ち上げる
iex --name node01@192.168.0.2 --cookie hoge
node02で以下のプロセスを立ち上げる
iex --name node02@192.168.0.3 --cookie hoge
node01のプロセスから以下を実行して接続する
iex(node02@192.168.0.2)1> Node.connect :"node02@192.168.0.3" true iex(node02@192.168.0.2)2> Node.list [:"node02@192.168.0.3"]
別ノードで実行する方法
iex(node02@192.168.0.2)3> Node.spawn :"node02@192.168.0.3", fn -> IO.inspect Node.self end :"node02@192.168.0.3" #PID<11251.119.0>