Pytorch の軽量ラッパー、skorch で回帰・分類を試してみた
Pytorch の軽量ラッパー、skorch を使って教師あり学習の回帰・分類を試してみたのでまとめました。
skorch は scikit-learn のように簡単に使うことができるそうです。
公式ドキュメント
参照した公式のサンプルコード
公式のGitHub
ブログ中ではコードの詳細は割愛しています。
コードは全体については、Google Colab はこちら、GitHub はこちら。
なお、ブログ中では触れていませんが、説明変数は StandardScaler() で標準化しています。
回帰
scikit-learn のボストンの住宅の中古価格のデータセットを使用します。
データセットの中身についてはこちらの記事をご参照ください。
ざっと目的変数の分布を確認すると、以下のとおり。
fig = plt.figure(figsize=(16, 8))
plt.plot(y, label="実績")
plt.legend()
説明変数 X、目的変数 y の型変換
説明変数 (X) は float型にする必要があるようです。
目的変数 (y) は、float型にし、reshapeで2次元の配列 (要素数, 1)に変換する必要があるようです。
X = X.astype(np.float32)
y = y.reshape(-1, 1)
y = y.astype(np.float32)
モデルの作成
回帰用のニューラルネットのクラスを作成します。
なお、ボストンの住宅価格のデータセットは、説明変数が13個なので入力層は13、回帰なので出力層は1にする必要があります。
class RegressorModule(nn.Module):
def __init__(
self,
num_units=20,
nonlin=F.relu,
):
super(RegressorModule, self).__init__()
self.num_units = num_units
self.nonlin = nonlin
self.dense0 = nn.Linear(13, num_units) # trainデータの説明変数の数に合わせる
self.nonlin = nonlin
self.dense1 = nn.Linear(num_units, 10)
self.output = nn.Linear(10, 1)
def forward(self, X, **kwargs):
X = self.nonlin(self.dense0(X))
X = F.relu(self.dense1(X))
X = self.output(X)
return X
TensorBoard 表示に必要な設定
Pytorch で TensorBoard を使うために必要な SummaryWriter 関数を利用します。
また、skorch には TensorBoard で使えるコールバック関数があるので利用します。
from torch.utils.tensorboard import SummaryWriter
# TensorBoard に出力するための log を格納するフォルダの指定
writer_reg = SummaryWriter(log_dir="./logs_reg")
# callbackの作成
from skorch.callbacks import TensorBoard
torch_callback_reg = TensorBoard(writer_reg, close_after_train=True)
処理スピードを上げるために、Google Colab で GPU を使用するために device='cuda' を指定しています。
GPU を使用しない、出来ない場合にはコメントアウトしましょう。
from skorch import NeuralNetRegressor
net_regr = NeuralNetRegressor(
RegressorModule,
max_epochs=500,
lr=0.01,
callbacks=[torch_callback_reg], # TensorBoard用の設定
device='cuda' # GPUを使用しない場合はコメントアウトが必要
)
学習
net_regr.fit(
X_train,
y_train,
)
tensorboard の表示
TensorBoard で読み込むログフォルダは、先ほど SummaryWriter() で指定したものを使用します。
%load_ext tensorboard
%tensorboard --logdir ./logs_reg
こんな感じで学習の進み方を可視化できました。
予測と評価
予測データは scikit-learn の要領で作成できるのでとても簡単です。
# 予測データの作成
y_pred = net_regr.predict(X_test)
# 評価
from sklearn.metrics import r2_score
r2_score(y_test, y_pred)
適当に作ったニューラルネットでも R2スコアが80%以上と比較的いい結果になりました。
0.8332760985274763
精度が思うように出ない場合は、NeuralNetRegressor のエポック数(max_epochs)や学習率(lr)、RegressorModule クラスの層の形状を調整してみましょう。
実績と予測をグラフで比較してやると以下のような感じです。
分類
scikit-learn のアヤメのデータセットを使用します。
データセットの中身についてはこちらの記事をご参照ください。
ざっと目的変数の分布を確認すると、以下のとおり。
fig = plt.figure(figsize=(12, 8))
plt.hist(y, label="実績")
plt.legend()
説明変数 X、目的変数 y の型変換
説明変数 (X) は回帰と同様にfloat型にする必要があるようです。
目的変数 (y) は回帰と違いint型にし、次元も1次元のままでよいようです。
X = X.astype(np.float32)
y = y.astype(np.int64)
モデルの作成
分類用のニューラルネットのクラスを作成します。
なお、アヤメのデータセットは、説明変数が4個なので入力層は4、出力層は目的変数のカテゴリ数3にする必要があるようです。
class ClassifierModule(nn.Module):
def __init__(
self,
num_units=20,
nonlin=F.relu,
dropout=0.5,
):
super(ClassifierModule, self).__init__()
self.num_units = num_units
self.nonlin = nonlin
self.dropout = dropout
self.dense0 = nn.Linear(4, num_units) # trainデータの説明変数の数に合わせる
self.nonlin = nonlin
self.dropout = nn.Dropout(dropout)
self.dense1 = nn.Linear(num_units, 10)
self.output = nn.Linear(10, 3) # trainデータの被説明変数の数に合わせる
def forward(self, X, **kwargs):
X = self.nonlin(self.dense0(X))
X = self.dropout(X)
X = F.relu(self.dense1(X))
X = F.softmax(self.output(X), dim=-1)
return X
なお、以降TensorBoard 使用に必要な部分は回帰と同様なので割愛します。
from skorch import NeuralNetClassifier
net = NeuralNetClassifier(
ClassifierModule,
max_epochs=500,
lr=0.1,
device='cuda', # GPUを使用しない場合はコメントアウトが必要
)
学習
net.fit(X_train,
y_train)
予測と評価
iris のデータセットは3項分類なので、f1スコアの引数に average='micro' を追加してやります。
# 予測データの作成
y_pred = net.predict(X_test)
# 評価
from sklearn.metrics import f1_score
f1_score(y_test, y_pred, average='micro')
1.0
これはたまたまだと思いますが、なんと完全に予測結果が一致しています。
実績と予測をグラフで比較してやると以下のような感じです。
その他、MNISTデータセットを使った学習や、転移学習、自然言語処理についてもチュートリアルがあるようなので、引き続き試したいと思います。
以上になります、最後までお読みいただきありがとうございました。
Discussion