ホーム  >   ブログ  >   PyTorchのautogradと仲良くなりたい

2017-09-23

PyTorchのautogradと仲良くなりたい

  寄付で活動を支援する   一杯のコーヒーを贈る

このエントリーをはてなブックマークに追加

(希望)

せっかくEuroScipy 2017でFacebook AI researchのSoumith Chintala氏から直に PyTorch のお話を聞いたので、触ってみるしかないぞ!と思いました。

特に、PyTorchのウリだと言っていた autograd(自動微分)が気になるので、まずは公式チュートリアルから入門してみる。

x という変数を requires_grad=True オプション付きで定義する。値は1:

import torch
from torch.autograd import Variable
# [x1, x2; x3, x4] = [1, 1; 1, 1]
x = Variable(torch.ones(2, 2), requires_grad=True)

"PyTorch is numpy alternative" と言うだけあって、配列(テンソル)操作は困らない。

そして一次関数 y = x + 1 を定義:

# [y1, y2; y3, y4] = [x+2, x+2; x+2, x+2]
y = x + 2

さらに y を使って二次関数をつくる:

# zi = 3 * (xi + 2)^2
z = y * y * 3

コメントの通り、z = y * y * 3 を展開すれば z = (x + 2) * (x + 2) * 3 です。

関数の出力値 out を適当な値 z.mean() とする。z の各要素が zi = 3 * (xi + 2)^2 だったので、 その平均値は:

# out = 1/4 * (z1 + z2 + z3 + z4)
#     = 1/4 * (3 * y1^2 + 3 * y2^2 + 3 * y3^2 + 3 * y4^2)
#     = 1/4 * (3 * (x1 + 2)^2 + 3 * (x2 + 2)^2 + 3 * (x3 + 2)^2 + 3 * (x4 + 2)^2)
out = z.mean()

out の勾配:

# d(out)
out.backward()

x について:

# d(out) / d(xi) = 1/4 * 2 * 3 * (xi + 2) = 3/2 * (xi + 2)
print(x.grad)  # => [4.5, 4.5; 4.5, 4.5]

最初に xi = 1 としていたので、 xi.grad = 3/2 * (xi + 2) = 3/2 * (1 + 2) = 4.5 ということになる。

なるほど、autograd.Variable.backward() がキモらしい。そしてこのチュートリアルだけでも、PyTorchの『線形なコードフローを推奨する』という思想が強く実感できる。

ここでもうひとつ、実践的な例としてロジスティック回帰による Bag-of-Words の二値分類を試してみる。

詳細は割愛しつつ、肝心のSGDによる学習の部分のコードを抜き出してみる:

# this classifier outputs log probs for input Bag-of-Words vector
model = BoWClassifier(NUM_LABELS, VOCAB_SIZE)

# negative log likelihood loss
# input will be a pair of <log probs computed by model, target label>
loss_function = nn.NLLLoss()

# SGD optimizer for model parameters
optimizer = optim.SGD(model.parameters(), lr=0.1)

for epoch in range(100):
    for sentence, label in train:
        # clear accumulated gradients
        model.zero_grad()

        # create input BoW vector and target variable as torch.autograd.Variable
        bow_vec = autograd.Variable(make_bow_vector(sentence))
        target = autograd.Variable(make_target(label))

        # run forward pass (i.e., prediction)
        log_probs = model(bow_vec)

        # compute loss
        loss = loss_function(log_probs, target)

        # gradient of loss
        loss.backward()

        # update model parameters
        optimizer.step()

(変数名、コメントなど少し変えたり簡略化したりした)

  • BoWベクトルの入力に対して log Softmax を予測値として返す分類モデル model を、
  • 負の対数尤度 nn.NLLLoss() を損失関数として、
  • 確率的勾配降下法 optim.SGD() によって学習している。

勾配降下法なので、パラメータは損失 loss の偏微分に基づいて毎ステップ更新される。これが27行目 loss.backward() の仕事ということに。わかってきた。

しかし線形なコードフロー、気持ちはとてもよく分かるんだけど、optimizerloss を渡したわけでもないのに loss.backward() => optimizer.step() でパラメータが更新されるのが気持ち悪い…。

EuroSciPy 2017のKeynoteで語られていたことの背景はここまでの内容で十分つかめるけど 1、autogradと仲良くなれる日はまだまだ遠そうだなぁと思うのでした。

次のステップはなんだろう。PyTorch + autograd で Matrix Factorization でも実装してみましょうか。

1. PyTorchの思想や、高階微分の実装といった今後の計画など、実際に触ってみて「確かにそうだよね」という気持ちになった。

  シェアする

このエントリーをはてなブックマークに追加

  カテゴリ

プログラミング 機械学習

  あわせて読みたい

2017-10-14
PyTorchでもMatrix Factorizationがしたい!
2017-09-02
EuroSciPy 2017に参加してしゃべってきた
2017-05-14
推薦システムのためのOSSたち

  もっと見る

最終更新日: 2022-01-18

  書いた人: たくち

たくちです。長野県出身、カナダ・バンクーバー在住のソフトウェアエンジニア。これまでB2B/B2Cの各領域で、Web技術・データサイエンス・機械学習のプロダクト化および顧客への導入支援・コンサルティング、そして関連分野のエバンジェリズムに携わってきました。現在はフリーランスとして活動を続けつつ、アフリカ・マラウイにて1年間の国際ボランティアに従事中。詳しい経歴はレジュメ を参照ください。いろいろなまちを走って、時に自然と戯れながら、その時間その場所の「日常」を生きています。ご意見・ご感想およびお仕事のご相談は [email protected] まで。

  寄付で活動を支援する   一杯のコーヒーを贈る

  免責事項

  • Amazonのアソシエイトとして、当サイトは amazon.co.jp 上の適格販売により収入を得ています。
  • 当サイトおよび関連するメディア上での発言はすべて私個人の見解であり、所属する(あるいは過去に所属した)組織のいかなる見解を代表するものでもありません。
  • 当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、個人ブログという性質上、誤情報や客観性を欠いた意見が入り込んでいることもございます。いかなる場合でも、当サイトおよびリンク先に掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。
  • その他、記事の内容や掲載画像などに問題がございましたら、直接メールでご連絡ください。確認の後、対応させていただきます。