人工知能の技術

PyTorchの基礎①: ニューラルネットワークをTensorで実装

こんにちは。現役エンジニアの三年坊主(@SannenBouzu)です。

エンジニア歴は6年。うち2年以上のAI開発を経験し、Web技術とAI技術をバランスよく習得してきました。

PyTorchに限りませんが、ネットの情報を見よう見まねでコードを書くと、「とりあえずコピペしたコードは動くけど、正直なんで動くか分かってないので、自分の用途に合わせて実装するのが難しい」という状況になりがちですよね。

今回は、PyTorchの基礎の理解を深めるため、PyTorchのTensorを使って、ごくごく簡単なニューラルネットワークを一から実装していきます。

公式チュートリアル What is torch.nn really? を掘り下げていきます。

この記事では torch.nn を一切使わないので、ニューラルネットワークの実装で普段なんとなく使っているtorch.nnが、背後でどのような処理をしているのか分かります。

その知識は、自然言語処理、画像処理など、幅広い分野に応用することができるでしょう。

この記事はこんな方におすすめ!

  • 必要最低限、PyTorchの基礎を理解したい方
  • PyTorchの理解を深めて、自然言語処理や画像処理などに応用したい方
Pythonを快適に使いこなすMac環境【現役エンジニアおすすめはPro 13インチ】MacでPythonプログラミングをしたい人向けに、現役エンジニアおすすめMac環境を紹介します。2011年からMacBook Proを使い続け、Mac買い替えやPythonインストールを経験している私が実体験に基づいて説明します。ぜひご覧ください。...

MNISTのデータを準備する

0から9まで、手書き数字の画像データセットとして有名な、MNISTを使います。

 

pickle形式で保存されたデータを読み取ります。

 

28*28の画像が784次元のNumPy ndarrayに格納されているので、reshapeして可視化しましょう。

 

NumPy ndarrayはそのままではPyTorchのモデル(今回はニューラルネットワーク)に入力できないので、mapで一括してtorch.tensorを適用し、Tensor型に変換します。

  • torch.as_tensor や、(ndarrayと分かっているので) torch.from_numpy でもOK
  • 今回の場合、これらの方法はTensorのコピーを行わない

 

 

 

ニューラルネットワークをTensorで実装する(torch.nnを使わない)

入力層784次元、出力層10次元の、シンプルなニューラルネットワークを実装してみましょう。隠れ層もありません。

Xavierの初期化という方法を使って、重みに適度なバラつきを持たせて学習を安定させます。

この初期化の後に、require_grad_()を使って勾配を記録させます。

 

log softmax

入力されるTensorについて、重みとの内積(@)にバイアスを加えた結果に対して、log softmaxを計算します。

 

サイズ64のミニバッチを、modelに入力すると、予測結果が得られます。

 

10個の値は、入力した画像テンソルがそれぞれ0から9の数字である(とモデルが予測する)「確率」とみなすことができ、この値が一番大きい数字が予測結果ということになります。

今回は「9」ですが、まだ何も学習しておらず、いわば「当てずっぽう」なので、今は気にしなくて大丈夫です。

 

 

log softmax【段階を追って深掘り】

log_softmax 関数で、どのような処理が行われているか、段階を踏んで確認していきます。

まず、Tensorのサイズを確認。

 

具体的に、x_trainから最初の3つのデータを使ってみます。

 

log_softmax 関数の中での処理を、一つずつ適用した結果はこちら。

  • sum(input, dim, keepdim=False, *, dtype=None): 指定した次元について、入力されたTensorのすべての要素の合計を返す
  • unsqueeze(input, dim): 指定した位置に、「次元1」を挿入した新しいTensorを返す

 

 

 

損失関数 loss function (negative log-likelihood)

 

学習を行う前の、損失関数の値を確認します。

 

損失関数 loss function (negative log-likelihood)【段階を追って深掘り】

クラス数C=10、データ数N=3の小さなデータを使って、この関数の処理を一つずつ追っていきます。

  • 対数は取らず負の符号は取り、ベクトルの重み付き平均を計算する
  • 「log」と言っているのは、入力されるpredsがlog_softmaxの結果(対数)のため

 

 

 

正解率 accuracy

 

学習を行う前の、正解率の値を確認します。

学習をしていないモデルは、ランダム・当てずっぽうな予測をするしかないので、当然、低いですね。

 

正解率 accuracy【段階を追って深掘り】

再び、クラス数C=10、データ数N=3の小さなデータを使って、この関数の処理を一つずつ追っていきます。

  • 予測と正解が一致しているデータをカウント
  • この例では、3つのデータのどの予測も正解と一致していないので、0

 

 

 

学習

いろいろと寄り道しましたが、いよいよモデルを学習させていきます。

  • データ数 bs のミニバッチを (xb と yb) 取得する
  • モデルに xb を入力して予測結果を得る
  • 損失を計算する
  • loss.backward() でモデルのパラメータ(weights と bias)の勾配を更新する 

 

 

学習前と比べて、損失関数の値が減少、正解率が増加したことを確認します。

 

まとめ:torch.nnを使わなくてもニューラルネットワークを実装できるけど面倒くさい

簡単なニューラルネットワークを、PyTorchのTensorを使って、一から実装してきました。

流れを一つ一つ追っていったので、処理の中身については理解が深まりましたが、実際にモデルを学習させるとなると、いくつか「面倒くさい」ポイントがありましたよね。

  • 損失関数まわりを自分で定義した (log_softmax, negative log likelihood)
  • モデルのパラメータ(weights と bias)を手動で一つずつ更新した
  • 重みとバイアスの適用 (xb @ weights + bias) を自分で定義した
  • 入力xとラベルyをそれぞれ別々にスライスしてミニバッチを生成した
  • 入力が、784次元(28*28)限定

torch.nnを使えば、こうした点を改善して、もっと柔軟で、分かりやすく、実用的な形に実装できます。

別の記事にて、今回のコードをtorch.nnを活用して書き換える内容を紹介します。

 

 

▼経験の棚卸しで納得のキャリアを▼
▼自宅で簡単・お得にふるさと納税▼
【期間限定】無料登録でプレゼント

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください