Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

東工大 traP Kaggle班 機械学習講習会 2024

abap34
July 18, 2024

東工大 traP Kaggle班 機械学習講習会 2024

東京工業大学デジタル創作同好会 traP Kaggle班 が2024年に実施した 「機械学習講習会」 の資料です。
各回ごとの資料、補足資料、そのほか各種リンク等は https://abap34.github.io/ml-lecture/ で閲覧可能です。

abap34

July 18, 2024
Tweet

More Decks by abap34

Other Decks in Education

Transcript

  1. この資料は 東京工業大学デジタル創作同好会 traP Kaggle班 で 2024年に実施した 「機械学習講習会」の資料です. 機械学習に初めて触れる学部一年生のメンバーが 1. 基本的な機械学習のアイデアを理解

    して, 2. 最終的にニューラルネットを実際の問題解決に使えるようになること を目指しています. (講習会自体については https://abap34.github.io/ml- lecture/supplement/preface.html をみてください) この資料について 2 / 420
  2. 目次 [1] 学習 ▶︎ この講習会について ▶︎ 学習とは? ▶︎ 損失関数 ▶︎

    トピック: なぜ"二乗"なのか [2] 勾配降下法 ▶︎ 関数の最小化 ▶︎ 勾配降下法 [3] PyTorch と自動微分 ▶︎ PyTorch の紹介 ▶︎ Tensor型と自動微分 ▶︎ トピック: 自動微分のアルゴリズムと実 装 [4] ニューラルネットワークの構造 ▶︎ 複雑さを生むには? ▶︎ 「基になる」関数を獲得する ▶︎ ニューラルネットワークの基本概念 ▶︎ トピック: 万能近似性と「深さ」 [5] ニューラルネットワークの学習と評 価 ▶︎ DNN の学習の歴史 ▶︎ 初期化 ? ▶︎ 確率的勾配降下法 ▶︎ さまざまなオプティマイザ ▶︎ バリデーションと過学習 [6] PyTorch による実装 ▶︎ データの前処理 ▶︎ モデルの構築 ▶︎ モデルの学習 ▶︎ モデルの評価 [7] 機械学習の応用, データ分析コン ペ ▶︎ データ分析コンペの立ち回り ▶︎ EDA ▶︎ CV と shake ▶︎ ハイパーパラメータのチューニング 3 / 420
  3. この資料を管理しているレポジトリは https://github.com/abap34/ml-lecture です。 誤りのご指摘などはこちらの Issue または https://twitter.com/abap34 までご連絡ください. 補足資料なども含めてまとめたものを https://abap34.com/trap_ml_lecture.html から確認できま

    す. この資料のリンクにはサークルメンバー以外がアクセスできないものが含まれています. (oj.abap34.com, dacq.abap34.com など) オンラインジャッジは https://github.com/abap34/ml-lecture-judge コンペプラットフォームは https://github.com/abap34/DacQ-v2 を動かしています. どちらもかなり未成熟ですが, 基本的なオンラインジャッジの機能と, データ分析コンペプラッ トフォームの機能を提供しています.これらをホストすることで同等の環境を構築することがで きます. そのほか何かあれば https://twitter.com/abap34 までご連絡ください. 各種リンク,注意など 4 / 420
  4. 第1回 │ 学習 第2回 │ 勾配降下法 第3回 │ 自動微分 第4回

    │ ニューラルネットワークの構造 第5回 │ ニューラルネットワークの学習と評価 第6回 │ PyTorch による実装 第7回 │ 機械学習の応用, データ分析コンペ おしながき 10 / 420
  5. Python を使います 慣れている人へ → Jupyter Notebook と numpy, matplotlib, scipy,

    PyTorch あたりのライブラリを使 えるようにしておいてください 慣れていない人へ → https://abap34.github.io/ml-lecture/supplement/colab.html をみて Google Colaboratory の使い方を覚えておいてください 使うプログラミング言語 13 / 420
  6. 1.Pythonを使った初歩的なプログラミング if文, for文, 関数 など 外部パッケージの利用 (そこまで高度なことは求めません ググり力とかの方が大事) 2.数学の初歩的な知識 基本的な行列の演算や操作

    (積,転置など) 基本的な微分積分の知識 (偏微分など) (1年前期の (線形代数) + (微分積分のさわり) くらい) 使うプログラミング言語や前提知識など 14 / 420
  7. 機械学習 で 人工知能 を実現 ( スーパーカー で,爆速移動 を実現) 機械学習 or

    AI? ここでは一つの定義を紹介しましたが, 実際この二つの言葉に明確に定義や合意があるわけではないです. 手法を厳密に分類してもあまり嬉しいことはないと思いますが, とりあえずこの講習会ではこういう形で整理してみることにします. 19 / 420
  8. 売り上げ = (気温) となる関数 を作りたい. ⇨ 一旦話を簡単にするために 「 (気温) =

    気温 + 」 のかたちであることにしてみる. 線形回帰 29 / 420
  9. を変えることでモデル の具体的な形が変わった! このように各モデルが固有に持ってモデル自身の性質を定める 数を 「パラメータ • • • • •

    」という. ( は をパラメータとして持つ ) ⬇︎ の構造を決めておけば... 「 の推定 のパラメータの推定」 パラメータ 関数 がパラメータ を持つことを陽に示すために, と書くことがあります. 今回の場合は となります. 32 / 420
  10. アイスの売り上げを予測するには, 気温から売り上げを予測する 「関数」を構築するのが必要であった. いったん, 今回は関数の形として (一次関数) に限って,関数を決め ることにした. この関数は, パラメータとして

    をもち, を変えることで 性質が変わるのがわかった これからやる仕事は, 「 をいい感じのものにする」ことで「いい感じの を作る」こと ちょっとまとめ 33 / 420
  11. 平均二乗誤差(Mean Squared Error) : 実際の値 (確定値) ... 過去のアイスの売り上げ : モデル :

    入力データ (確定値) ... 過去の気温 平均二乗誤差(Mean Squared Error) なぜ差を二乗するのか疑問に思った人もいるかもしれません.  全てをここで話すと情報量過多なので一旦置いといてあとで軽く議論します.(末尾の付録) 38 / 420
  12. Q. 損失は何の関数? (何を動かして損失を小さくする?) 各 は変数みたいな見た目だけど 「もう観測された確定値」 • • • •

    • • • • • • • • 何を動かして損失を小さくする? ものすごく進んだ話: たまに「入力データ」っぽいものに当たるものについても変数とみることもあります. 自分の知っている話だと DeepSDF という三次元形状を表現する NN では latent code と呼ばれる物体固有の表現を表すベクトルも変化させて損失関数を最小化していました. 41 / 420
  13. アイスの売り上げを予測するには, 気温から売り上げを予測する 「関数」を構築するのが必要であった. いったん, 今回は関数の形として (一次関数) に限って,関数を決め ることにした. この関数は, パラメータとして

    をもち, を変えることで 性質が変わるのがわかった モデルの「よさ」のめやすとして 「損失関数」を導入した パラメータを変えることで損失関数を最小化する過程のことを「学習」と呼ぶ まとめ 46 / 420
  14. アイスの売り上げを予測するには, 気温から売り上げを予測する 「関数」を構築するのが必要であった. いったん, 今回は関数の形として (一次関数) に限って,関数を決め ることにした. この関数は, パラメータとして

    をもち, を変えることで 性質が変わるのがわかった モデルの「よさ」のめやすとして, 「損失関数」を導入した パラメータを変えることで損失関数を最小化する過程のことを「学習」と呼ぶ まとめ 51 / 420
  15. 簡単な数式の操作で解けた! 機械的に書くなら 「 を最小にする は 」 という公式を使った プログラムに起こすと... # ax^2

    + bx + c を最小にする x を返す関数. def solve(a, b, c): return -b / (2 * a) どう「解けた」?? 55 / 420
  16. A. No. 厳密に最小値を得る必要はない 数学の答案で最小値 1 になるところを 1.001と答えたら当然 一方 「誤差 1」

    が 「誤差1.001」 になってもほとんど変わらない 効いてくる条件① 67 / 420
  17. 勾配降下法 関数 と初期値 が与えられたとき, 次の式で を更新するアルゴリズム ( は学習率と呼ばれる定数) 勾配降下法 正確にはこれは最急降下法と呼ばれるアルゴリズムで,

    「勾配降下法」は勾配を使った最適化手法の総称として用いられることが多いと思います. (そこまで目くじらを立てる人はいないと思いますし, 勾配降下法あるいは勾配法と言われたらたいていの人がこれを思い浮かべると思います.) 77 / 420
  18. from math import exp x = 3 # (注意: $\eta$

    は 学習率 (learning rate) の略である lr としています.) lr = 0.0005 # 最小化したい関数 def f(x): return x ** 2 + exp(-x) # f の x での微分係数 def grad(x): return 2 * x - exp(-x) Pythonによる実装 85 / 420
  19. をコードに起こす for i in range(10001): # 更新式 x = x

    - lr * grad(x) if i % 1000 == 0: print('x_', i, '=', x , ', f(x) =', f(x)) x_ 0 = 2.997024893534184 , f(x) = 9.032093623218246 x_ 1000 = 1.1617489280037716 , f(x) = 1.6625989669983947 x_ 2000 = 0.5760466279295902 , f(x) = 0.8939459518186053 x_ 3000 = 0.4109554481889124 , f(x) = 0.8319008499233866 ... x_ 9000 = 0.3517515401706734 , f(x) = 0.8271840265571999 x_ 10000 = 0.3517383210080008 , f(x) = 0.8271840261562484 Pythonによる実装 86 / 420
  20. ⇨ なるべく局所最適解に ハマりまくらない • • • • • • •

    • ように色々と工夫 (詳しくは第5回) Momentum AdaGrad マイナーチェンジ 90 / 420
  21. 結論から言うと... PyTorchを使うと微分ができる. >>> x = torch.tensor(2.0, requires_grad=True) >>> def f(x):

    ... return x ** 2 + 4 * x + 3 ... >>> y = f(x) >>> y.backward() >>> x.grad tensor(8.) ( の における微分係数 が計算されている) 自動微分 103 / 420
  22. 例) 新しい車を開発するときも,部品は大体同じ,組み立ても大体同じ ⇩ 毎回同じことをみんながそれぞれやるのは面倒 ⇩ 共通基盤 • • • •

    を提供するソフトウェアの需要がある そもそもPyTorchとは? 〜深層学習フレームワーク〜 105 / 420
  23. TensorFlow (主に) Googleが開発したフレームワーク 産業界で人気 (が, 最近はPyTorchに押され気味) PyTorch (主に) Facebookが開発したフレームワーク 研究界で人気

    (最近はみんなこれ?) Keras いろんなフレームワークを使いやすくしたラッパー (おもに TensorFLow) とにかくサッと実装できる JAX/Flax, Chainer, MXNet, Caffe, Theano, ... どの組み立て機を使う? 有名なフレームワークたち 106 / 420
  24. 今回は PyTorch を使います! 高速な実行 非常に柔軟な記述 大きなコミュニティ 超充実した周辺ライブラリ サンプル実装の充実 (← 重要!!)

    なので 大体の有名フレームワークにそこまで致命的な速度差はなく, 記述に関しては好みによ るところも多いです.PyTorchの差別化ポイントは, 有名モデルの実装サンプルが大体存 在するという点です. 実際に論文を読んで実装するのは骨の折れる作業なので, サンプルが充実していのはと ても大きな利点です. 108 / 420
  25. スカラー: 添字 個で値が決まる 階のテンソル ベクトル: 添字 個で値が決まる 階のテンソル ( v

    = [1, 2, 3], v[0] = 1 ) 行列: 添字 2 個で値が決まる 階のテンソル ( M = [[1, 2], [3, 4]], M[0][0] = 1 ) ⇩ 例えば T = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] は 階のテンソル ( T[0][0][0] = 1 ) Tensor とは? 112 / 420
  26. 例) RGB 画像は 階のテンソル! 画素の 番目の色の強 さ ⇩  枚の画像をまとめたものは 階の

    テンソル. 番目の画像の 画素 の 番目の色の強さ テンソルの例 画像は Quantifying Blur in Color Images using Higher Order Singular Values - Scientific Figure on ResearchGate. Available from: https://www.researchgate.net/figure/3rd-order-Tensor-representation-of-a- color-image_fig2_307091456 より 113 / 420
  27. torch.tensor(data, requires_grad=False) data : 保持するデータ(配列っぽいものならなんでも) リスト, タプル, NumPy配列, スカラ, ...

    requires_grad : 勾配 (gradient)を保持するかどうかのフラグ デフォルトは False 勾配の計算(自動微分)を行う場合は True にする このあとこいつを微分の計算に使いますよ〜という表明 Tensor 型のつくりかた 114 / 420
  28. >>> x = torch.tensor(2.0, requires_grad=True) というスカラを保持する Tensor 型のオブジェクトを作成 >>> x

    = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) というベクトルを保持する Tensor 型のオブジェクトを作成 Tensor 型 かつては自動微分には Variable という名前の型が使われていて, (現在は Tensor 型に統合)  Tensor と数学の変数の概念にある程度の対応があることがわかります. 115 / 420
  29. >>> x = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], requires_grad=True)

    という行列を保持する Tensor 型のオブジェクトを作成 ( requires_grad=True とすれば, 勾配計算が可能な Tensor 型を作成できる) Tensor 型 116 / 420
  30. 1, 2, 3: 講義資料を遡って, torch.tensor の第一引数と作成される Tensor 型の対応を 見比べてみましょう. 4:

    Pythonのエラーは, ~~たくさん書いてある~ ~~Error: {ここにエラーの端的な内容が書いてある} という形式です."~~Error"というところのすぐ後に書いてある内容を読んでみましょ う. 演習1 ヒント 119 / 420
  31. 1~3. # 1 x = torch.tensor(3.0, requires_grad=True) # 2 x

    = torch.tensor([3.0, 4.0, 5.0], requires_grad=True) # 3 x = torch.tensor([[3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], requires_grad=True) 次のページへ 演習1 解答 120 / 420
  32. 4. x = torch.tensor(3, requires_grad=True) としてみると RuntimeError: Only Tensors of

    floating point and complex dtype can require gradients と出力されます. これは「勾配が計算可能なのは浮動小数点数型と複素数型を格納する Tensor のみである」 という PyTorch の仕様によるエラーです. 演習1: 解答 121 / 420
  33. Tensor 型は 「数」なので当然各種演算が可能 x = torch.tensor(2.0, requires_grad=True) 例) 四則演算 x

    + 2 # -> tensor(4., grad_fn=<AddBackward0>) x * 2 # -> tensor(4., grad_fn=<MulBackward0>) Tensor 型に対する演算 122 / 420
  34. 平方根を取ったり や を計算することも可能 torch.sqrt(x) # -> tensor(1.4142, grad_fn=<SqrtBackward0>) torch.sin(x) #

    -> tensor(0.9093, grad_fn=<SinBackward0>) torch.exp(x) # -> tensor(7.3891, grad_fn=<ExpBackward0>) Tensor 型に対する演算 123 / 420
  35. 普通の Pythonの数値では, x = 2 y = x + 2

    print(y) # -> 4 y がどこから来たのかはわからない (値として を持っている だけで、他にはない) PyTorch と 自動微分 127 / 420
  36. 1. Tensor 型のオブジェクトをつくる x = torch.tensor(2.0, requires_grad=True) 2. 計算を行う y

    = x + 2 3. backward メソッドを呼ぶ y.backward() すると... backward による勾配計算 130 / 420
  37. 1. 変数 ( Tensor 型)の定義 2. 計算 3. backward() #

    1. 変数(`Tensor` 型)の定義 x = torch.tensor(2.0, requires_grad=True) # 2. 計算 y = x + 2 # 3. backward() y.backward() すると x.grad に計算された勾配が格納される. 自動微分の流れ 133 / 420
  38. 定義 計算 backward(), 定義 計算 backward(), 定義 計算 backward(), 定義

    定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 演習2: 100回唱えよう! 134 / 420
  39. 例1)  の微分 x = torch.tensor(2.0, requires_grad=True) y = y =

    torch.sin((x + 2) + (1 + torch.exp(x ** 2))) y.backward() print(x.grad()) # -> tensor(-218.4625) 例2) の微分( ) x = torch.tensor(2.0, requires_grad=True) y = x ** 2 z = 2 * y + 3 z.backward() print(x.grad) # -> tensor(8.) ... backward()した変数に対する勾配!(この場合はz) ありとあらゆる演算が自動微分可能 135 / 420
  40. x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) y = 2 *

    x[0] + 3 * x[1] + 4 * x[2] y.backward() print(x.grad) # -> tensor([2., 3., 4.]) と対応 ベクトル, 行列演算の勾配 136 / 420
  41. A = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], requires_grad=True) y

    = torch.sum(A) y.backward() print(A.grad) # -> tensor([[1., 1., 1.], # [1., 1., 1.]]) と対応 ベクトル, 行列演算の勾配 137 / 420
  42. x = torch.tensor(2.0, requires_grad=True) y = torch.tensor(3.0, requires_grad=True) z =

    2 * x + 4 * y z.backward() print(x.grad) # -> tensor(2.) print(y.grad) # -> tensor(4.) に対応 多変数関数の微分 138 / 420
  43. x = torch.tensor(2.0, requires_grad=True) def f(x): return x + 3

    def g(x): return torch.sin(x) + torch.cos(x ** 2) if rand() < 0.5: y = f(x) else: y = g(x) 実際に適用される演算は実行してみないとわからない... が, 適用される演算はどう転んでも微分可能な演算なのでOK ! (if 文があるから, for 文があるから, 自分が定義した関数に渡したから...ということは 関係なく, 実際に Tensor に適用される演算のみが問題になる) 注意: 実際に適用される演算さえ微分可能ならOK 139 / 420
  44. 抑えてほしいポイント 任意の(勾配が定義できる)計算を Tensor 型に対して適用すれば常に自動微分可 能 定義→計算→backward() の流れ ベクトル, 行列など任意の Tensor

    型について微分可能. 多変数関数の場合も同様 「実際に適用される演算」さえ微分可能ならOK 自動微分 140 / 420
  45. 1. x = torch.tensor(3.0, requires_grad=True) y = x ** 2

    + 2 * x + 1 y.backward() gx = x.grad print(gx.item()) # -> 8.0 演習3: 解答 スペースの都合上 import torch  を省略しています 142 / 420
  46. 2. import torch x1 = torch.tensor(1.0, requires_grad=True) x2 = torch.tensor(2.0,

    requires_grad=True) x3 = torch.tensor(3.0, requires_grad=True) y = x1**2 + x2**2 + x3**2 y.backward() print(x1.grad.item()) # -> 2.0 print(x2.grad.item()) # -> 4.0 print(x3.grad.item()) # -> 6.0 演習3: 解答 143 / 420
  47. 3. W = torch.tensor([[1.0, 2.0], [2.0, 1.0]]) x1 = torch.tensor([1.0,

    2.0], requires_grad=True) y = torch.matmul(torch.matmul(x1, W), x1) y.backward() gx = x1.grad print(*gx.numpy()) # -> 10.0 8.0 演習3: 解答 144 / 420
  48. の勾配降下法による最小値の探索 from math import exp x = 3 lr =

    0.0005 # xでの微分係数 def grad(x): return 2 * x - exp(-x) for i in range(10001): # 更新式 x = x - lr * grad(x) if i % 1000 == 0: print('x_', i, '=', x) 思い出すシリーズ: 勾配降下法のPyTorchによる実装 145 / 420
  49. これまでは,導関数 grad を我々が計算しなければいけなかった ⇨ 自動微分で置き換えられる! import torch lr = 0.01

    N = 10001 x = torch.tensor(3.0, requires_grad=True) def f(x): return x ** 2 - torch.exp(-x) for i in range(10001): y = f(x) y.backward() x.data = x.data - lr * x.grad x.grad.zero_() 勾配降下法のPyTorchによる実装 146 / 420
  50. < 微分係数を計算してください! ⇩ [いちばん素直な方法] を、小さい値で近似する def diff(f, x): h =

    1e-6 return (f(x + h) - f(x)) / h おまけ: 自動微分のアルゴリズム 149 / 420
  51. これでもそれなりに近い値を得られる. 例) の における微分係数 を求める. >>> def diff(f, x): ...

    h = 1e-6 ... return (f(x + h) - f(x)) / h ... >>> diff(lambda x : x**2, 2) 4.0000010006480125 # だいたいあってる 勾配の計算法を考える ~近似編 150 / 420
  52. 問題点①. 誤差が出る 1. 本来極限をとるのに小さい を とって計算しているので誤差が出る 2. 分子が極めて近い値同士の引き算に なっていて 桁落ちによって精度が大幅に悪化.

    問題点②. 勾配ベクトルの計算が非効率 1. 変数関数の勾配ベクトル を計算するには, 各 について「少し動かす→計算」 を繰り返すので 回 を評価する. 2. 応用では がとても大きくなり, の評価が重くなりがちなので これが 致命的 数値微分 1.3 勾配降下法と機械学習 152 / 420
  53. 演算は 計算グラフ とよばれる DAG で表現できる の計算グラフ  計算グラフ 単に計算過程を表しただけのものを Kantorovich グラフなどと呼び,

    これに偏導関数などの情報を加えたものを計算グラフと呼ぶような定義もあります. (伊里, 久保田 (1998) に詳しく形式的な定義があります) ただ, 単に計算グラフというだけで計算過程を表現するグラフを指すという用法はかな り普及していて一般的と思われます.そのためここでもそれに従って計算過程を表現す るグラフを計算グラフと呼びます. 154 / 420
  54. PyTorch も 計算と同時 に 計算グラフを構築 ( torchviz というライブラリを使う と可視化できる! )

    import torchviz x = torch.tensor([1., 2., 3.], requires_grad=True) y = torch.sin(torch.sum(x) + 2) torchviz.make_dot(y) 計算グラフ PyTorch のように計算と同時に計算グラフを構築する仕組みを define-by-run と呼び ます. これに対して計算前に計算グラフを構築する方法を define-and-run と呼びま す. かつての TensorFlow などはこの方式でしたが, 現在では define-by-run が主流で す. 「適用される演算のみが問題になる」という節からわかるように, この方法だと制 御構文などを気にせず柔軟な計算グラフの構築が可能になるからです. 一方で、静的に 計算グラフを作るのはパフォーマンスの最適化の観点からは非常にやりやすいという メリットもあります. 155 / 420
  55. z Mul x y Sub u Add v 変数 に対する

    による偏微分の 計算グラフ上の表現 から への全ての経路の偏微分の総積の総和 は から への全ての経路の集合. は変数 から変数 への辺を表す. 連鎖律と計算グラフの対応 161 / 420
  56. 1. 基本的な演算 を用意しておく. class Add: def __call__(self, x0: Tensor, x1: Tensor)

    -> Tensor: self.x0 = x0 self.x1 = x1 return Tensor(x0.value + x1.value, creator=self) def backward(self, gy): return gy, gy class Mul: def __call__(self, x0: Tensor, x1: Tensor) -> Tensor: self.x0 = x0 self.x1 = x1 return Tensor(x0.value * x1.value, creator=self) def backward(self, gy): return gy * self.x1, gy * self.x0 OO を使った典型的な自動微分の実装 163 / 420
  57. 1. 変数を表すオブジェクトを用意して おき、これの基本的な演算をオーバ ーライドする. class Tensor: def __init__(self, value): ...

    def __add__(self, other): return Add()(self, other) def __mul__(self, other): return Mul()(self, other) OO を使った典型的な自動微分の実装 164 / 420
  58. 和をとる 「基になる関数 • • • • • • 」 にどのような関数を選ぶべきか?

    三角関数? 多項式関数? 指数関数? もっと別の関数? これまでの我々のアプローチを思い出すと... 変化させるのが可能なところはパラメータにして, 学習で求める」 「基になる関数」はどう選ぶべきか? 188 / 420
  59. 演算を 回繰り返す ( 次元ベクトル → , → , → ,

    → 次元ベクトルへと変換されながら 計算が進んでいく) 合成 201 / 420
  60. 用語 意味 MLP (Multi Layer Perceptron) 全結合層のみからなるニューラルネットワーク DNN (Deep Neural

    Network) 複数の隠れ層を持つニューラルネットワーク ANN (Artificial Neural Network) 人工ニューラルネットワーク.本来の意味のニューラルネッ トワーク(動物の神経回路) と区別するためこういう名前が使 われることがある そのほかの用語たち 213 / 420
  61. 我々の学習手法は, というモデルの構造自体に直接依存している わけではなかった というモデルの構造では直線しか表現することができないので, 違う形を考えることにした 「基になる」簡単な関数の 合成 と 和 を考えることでかなり複雑な関数も表現で

    きることがわかった 「基になる」関数の選び方を考える上で, この関数自体もパラメータによって変化 させるモデルとしてニューラルネットワークを導入した ニューラルネットワークは非常に幅広い関数を表現できることがわかった 今日のまとめ 217 / 420
  62. ニューラルネットワークの表現能力は 1980年代後半 ~ 1990年代後半くらいまで 盛んに研究 いろいろな条件でいろいろな結果を得ている ここではおそらく最も有名である Cybenko による定理 [1]

    を紹介する 発展的話題:万能近似の(直感的な) 説明 • • [1] Cybenko, George. "Approximation by superpositions of a sigmoidal function." Mathematics of control, signals and systems 2.4 (1989): 303-314. 218 / 420
  63. は とすると が少しでも正ならば , そうでなければ になる. ⇨ を適当に調整すれば, 狙った点 で

    とすることができる. (例: なら ) さらに を負の非常に大きい数にすると, 逆のバージョンも作れる. 証明ステップ1 224 / 420
  64. この結果の主張: 十分幅が広い「隠れ層」が一つあれば十分 世の中の主張: たくさんの層があるNNがよく機能する   ⇩ なぜ? A. 層を深くすると指数関数的に表現力が上 がり, 幅を広くすると多項式的に表現力が上

    がる. [1] 「深さ」は必要? [1] Montufar, Guido F., et al. "On the number of linear regions of deep neural networks." Advances in neural information processing systems 27 (2014). 画像も同論文より 228 / 420
  65. 我々の学習手法は, というモデルの構造自体に直接依存している わけではなかった というモデルの構造では直線しか表現することができないので, 違う形を考えることにした 「基になる」簡単な関数の 合成 と 和 を考えることでかなり複雑な関数も表現で

    きることがわかった 「基になる」関数の選び方を考える上で, この関数自体もパラメータによって変化 させるモデルとしてニューラルネットワークを導入した ニューラルネットワークは非常に幅広い関数を表現できることがわかった 振り返りタイム 231 / 420
  66. Geoffrey Hinton DBN (Deep Belief Network) やオートエンコーダに関する 研究 [1][2] を通じて

    DNN の学習の安定化に大きく貢献 学習手法の進化 [1] Hinton, Geoffrey E., Simon Osindero, and Yee-Whye Teh. "A fast learning algorithm for deep belief nets." Neural computation 18.7 (2006): 1527-1554. [2] Hinton, Geoffrey E., and Ruslan R. Salakhutdinov. "Reducing the Dimensionality of Data with Neural Networks." Science, vol. 313, no. 5786, 2006, pp. 504-507. doi:10.1126/science.1127647. 236 / 420
  67. 活性化関数の進化 (ReLU) Dropout Batch Normalization オプティマイザの進化 (Adam, RMSprop ...) ⇩

    DNN の学習を比較的安定して行えるように 学習手法の進化 237 / 420
  68. Xavier (Glorot) の初期値 初期値の決め方 Glorot, Xavier, and Yoshua Bengio. "Understanding

    the difficulty of training deep feedforward neural networks." Proceedings of the thirteenth international conference on artificial intelligence and statistics. JMLR Workshop and Conference Proceedings, 2010. 245 / 420
  69. シグモイド関数はよくない性質 ( 勾配消失) がある! ⇨ 次第に が使われるようになる ⇩ ReLU 向けの初期値

    (導出は Xavier と一緒) He (Kaiming) の初期値 初期値の決め方 He, Kaiming, et al. "Delving deep into rectifiers: Surpassing human-level performance on imagenet classification." Proceedings of the IEEE international conference on computer vision. 2015. 252 / 420
  70. モデルの構造 (とくに活性化関数) に よって適切な初期値のとり方が変わ ってくる! 例) SIREN [1] という活性化関数に を使うモデルは

    がいいとされて いる 導出から自然にわかること [1] Sitzmann, Vincent, et al. "Implicit neural representations with periodic activation functions." Advances in neural information processing systems 33 (2020): 7462-7473. 画像も同論文より引用 253 / 420
  71. Batch Normalization 入力をミニバッチごとに正規化するレイヤー ⇨ 学習の効率化にかなり役立ち 初期化の影響を受けにくくする • • • •

    • • • • • • • • • • Batch Normalization Ioffe, Sergey, and Christian Szegedy. "Batch normalization: Accelerating deep network training by reducing internal covariate shift." International conference on machine learning. pmlr, 2015. 255 / 420
  72. 実は決定論的にやってもよい? ⇨ ZerO Initialization [1] 乱数生成をやめると再現性が向上してうれしい. おまけ: 「乱数」は初期値に必要か? [1] Zhao,

    Jiawei, Florian Schäfer, and Anima Anandkumar. "Zero initialization: Initializing neural networks with only zeros and ones." arXiv preprint arXiv:2110.12661 (2021). 256 / 420
  73. https://www.telesens.co/loss- landscape-viz/viewer.html で見て みよう! ( 実際に右の3次元空間上で探索しているわけで はないです!!!) NNの「損失平面」 Li, Hao,

    et al. "Visualizing the loss landscape of neural nets." Advances in neural information processing systems 31 (2018). 画像も同論文より 262 / 420
  74. 例) アイスの予測ができるモデルが完成した!!! 学習の際に使ったデータは {(20℃, 300円), (25℃, 350円), (30℃, 400円), (35℃,

    450円), (40℃, 500円)} ⇨ さぁこれを使ってアイスの値段を予測するぞ! ⇨ 来るデータは.... {22℃, 24℃, 25℃, } ※ 重要: これらのデータは学習段階では存在しない 学習した後のことを考えよう 279 / 420
  75. 学習データ { (20℃, 300円), (25℃, 350円), (30℃, 400円), (35℃, 450円),

    (40℃, 500円) } ⇩ 分割 学習データ { (20℃, 300円), (25℃, 350円), (30℃, 400円) } 検証用データ { (35℃, 450円), (40℃, 500円) } 未知のデータに対する性能を検証する 285 / 420
  76. 学習データ { (20℃, 300円), (25℃, 350円), (30℃, 400円) } のみで学習をおこなう

    ⇩ (35℃, 450円), (40℃, 500円)に対して推論を行い,誤差を評価 400円,500円と推論したとすると, 「検証用データに対する」平均二乗誤差は 未知のデータに対する性能を検証する 286 / 420
  77. 学習データ: { (20℃, 300円), (25℃, 350円), (30℃, 400円) } のみで学習!

    検証用データはパラメータの更新に使わず誤差の計算だけ ⇩ つまり 擬似的に 未知のデータ • • • • • • を作成して,「未知のデータに対する性能」を評価 未知のデータに対する性能を検証する 287 / 420
  78. 損失関数の値はあくまで「訓練データに対してこれくらいの誤差になるよ」とい う値 ほんとうに興味があるのは, 知らないデータに対してどれくらいうまく予測できる か これの検証のために擬似的に学習に使わない未知のデータを作り, 未知のデータに 対する予測の評価をする ちょっとまとめ バリデーションの手法や切り方についてはいろいろあり,

    話すとかなり長くなりますのでここでは割愛します. 例えば Cross Validation や時系列を意識した Validation, テストデータとバリデーションデータの性質を近づけるための手法などもあります。 詳しくは 8月に実施予定の講習会で扱われるはずです! 293 / 420
  79. 2019年の京大の研究 [1] 「過去の気温のデータから気温変化 を NN で予測して, 検証用データで 97% の精度で上がるか下がるかを的 中できるようになりました!」とい

    うもの 不適切なバリデーションの例 Ise, T., & Oba, Y. (2019). Forecasting Climatic Trends Using Neural Networks: An Experimental Study Using Global Historical Data. Frontiers in Robotics and AI, 6, 446979. https://doi.org/10.3389/frobt.2019.00032 302 / 420
  80. train.csv 学習に使うデータ train_tiny.csv ( 時間と説明の都合上 今日はこれを使います) 学習に使うデータの一部を取り出し,一部を削除 test.csv 予測対象のデータ test_tiny.csv

    ( 時間と説明の都合上 今日はこれを使います) 予測対象のデータの欠損値を埋めて,一部のカラムを削除 sample_suboldsymbolission.csv 予測の提出方式のサンプル (値はでたらめ) データ 314 / 420
  81. 1-0. データのダウンロード ⇩ 1-1. データの読み込み ⇩ 1-2. データの前処理 ⇩ 1-2.

    PyTorchに入力できる形に 全体の流れ1 ~モデルに入力するまで 316 / 420
  82. セルに以下をコピペして実行 !curl https://www.abap34.com/trap_ml_lecture/public-data/train_tiny.csv -o train.csv !curl https://www.abap34.com/trap_ml_lecture/public-data/test_tiny.csv -o test.csv !curl

    https://www.abap34.com/trap_ml_lecture/public-data/sample_submission.csv -o sample_submission.csv 1-0. データのダウンロード Jupyter Notebook では,先頭に ! をつけることで,シェルコマンドを実行できます. 317 / 420
  83. 左の > train.csv, test.csv, sample_submission.csv で表が見えるようになって いたら OK! 1-0. データのダウンロード

    今回のコンペのデータは ISCX NSL-KDD dataset 2009 [1] をもとに大きく加工したものを使用しています。 [1] M. Tavallaee, E. Bagheri, W. Lu, and A. Ghorbani, “A Detailed Analysis of the KDD CUP 99 Data Set,” Submitted to Second IEEE Symposium on Computational Intelligence for Security and Defense Applications (CISDA), 2009. 318 / 420
  84. pd.read_csv(path) で, path にあるcsvファイルを読み込める # pandas パッケージを `pd` という名前をつけてimport import

    pandas as pd # これによって, pandas の関数を `pd.関数名` という形で使えるようになる train = pd.read_csv("train.csv") test = pd.read_csv("test.csv") 1-1. データの読み込み パスはコンピュータ上のファイルやフォルダへの経路のことです. 今回は train.csv と test.csv がノートブックと同じ階層にあるので, train.csv と test.csv までの経路は,ファイル名をそのまま指定するだけで大丈夫です. ほかにも たとえば ../train.csv と指定すると ノートブックの一つ上の階層にある train.csv というファイルを読み込みます. 319 / 420
  85. 今まで ⇩ x = [1, 2, 3, 4, 5] y

    = [2, 4, 6, 8, 10] def loss(a): ... ⇩ 今回も入力と出力 (の目標) にわけておく 1-1. データの読み込み 321 / 420
  86. 機械学習モデルは 直接的には • • • • • 数以外 は扱えないので数に変換しておく. train_y

    = train['class'].map({ 'normal': 0, 'attack': 1 }) 1-1. データの読み込み 324 / 420
  87. 逆に, モデルに入力するデータは train から さっきの列 (と id ) を除いたもの! train.drop(columns=['カラム名'])

    を使うと train から「カラム名」という名前の 列を除いたもの を取り出せる ⇩ 今回は train.drop(columns=['id', 'class']) 1-1. データの読み込み 325 / 420
  88. train_x = train.drop(columns=['id', 'class']) test_x = test.drop(columns=['id']) ⇨ train_x にさっきの列と

    id を 除いたもの, test_x に id を除いた ものが入る 1-1. データの読み込み 326 / 420
  89. 標準化 ( は平均, は標準偏差) 1. 平均 のデータの全ての要素から を引くと,平均は 2. 標準偏差

    のデータの全ての要素を で割ると,標準偏差は ⇨ 標準化で 平均を0,標準偏差を1 にできる 1-2. データの前処理 初期化の際の議論を思い出すとこのようなスケーリングを行うことは自然な発想だと思います. NN の入力の標準化については, LeCun, Yann, et al. "E cient BackProp." Lecture Notes in Computer Science 1524 (1998): 5-50. にもう少し詳しく議論が載っていたので気になる 人は読んでみてください. 329 / 420
  90. scikit-learn というライブラリの StandardScaler クラスを使うと, 簡単に標準化できる! # sklearn.preprocessing に定義されているStandardScalerを使う from sklearn.preprocessing

    import StandardScaler scaler = StandardScaler() # 計算に必要な量 (平均,標準偏差) を計算 scaler.fit(train_x) # 実際に変換 train_x = scaler.transform(train_x) test_x = scaler.transform(test_x) 1-2. データの前処理 scalar.fit によって引数で渡されたデータの各列ごとの平均と標準偏差が計算され, scalar に保存されます. そして, scalar.transform によってデータが実際に標準化されます. 勘が いい人は「 test に対しても train_x で計算した平均と標準偏差を使って標準化しているけど大丈夫なのか?」と思ったかもしれないですね. 結論から言うとそうなのですが意図して います. ここに理由を書いたら信じられないくらいはみ出てしまったので, 省略します. 興味がある人は「Kaggleで勝つデータ分析の技術」p.124 などを参照してみてください. 330 / 420
  91. train_x test_x などを実行してみると,確かに何かしらの変換がされている! (ついでに結果がテーブルから単なる二次元配列 ( np.ndarray ) に変換されてる) 1-2. データの前処理

    最初のテーブルっぽい情報を持ったまま計算を進めたい場合は, train_x[:] = scaler.transform(train_x) のようにすると良いです. 331 / 420
  92. ので train_y もここで中身を取り出して np.ndarray にしておく. 1. train_y.values で 中身の値を取り出せる. 2.

    arr.reshape(-1, 1) で arr を の形に変換できる train_y = train_y.values.reshape(-1, 1) 1-2. データの前処理 np.ndarray のメソッド reshape はその名の通り配列の形を変えるメソッドです. そして -1 は「他の次元の要素数から自動的に決定する」という意味です. 例えば, の配列に対して .reshape(-1, 2) とすると にしてくれます. (2次元目が と確定しているので勝手に と定まる) 332 / 420
  93. sklearn.model_selection.train_test_split による分割 train_test_split(train_x, train_y, test_size=0.3, random_state=34) train_x , train_y :

    分割するデータ test_size : テストデータの割合 random_state : 乱数のシード 重要!! 1-2. データの前処理 - バリデーション 334 / 420
  94. scikit-learn の train_test_split を使うと簡単にデータを分割できる! from sklearn.model_selection import train_test_split train_x, val_x,

    train_y, val_y = train_test_split(train_x, train_y, test_size=0.3, random_state=34) 1-2. データの前処理 - バリデーション 335 / 420
  95. 乱数に基づく計算がたくさん ⇩ 実行するたびに結果が変わって, めちゃくちゃ困る ⇩ 乱数シードを固定すると, 毎回同じ結果になって 再現性確保 • •

    • • • 乱数シードを固定しよう!! 実際はそんな素朴な世の中でもなく, 環境差異であったり, 並列処理をしたとき (とく に GPU が絡んだとき) には単に乱数シードを固定するような見た目のコードを書いて も結果が変わりがちで, 困ることが多いです. 対処法もいろいろ考えられているので, 気になる人は jax の乱数生成の仕組みなどを調べてみると面白いかもしれません。 336 / 420
  96. ( train_x , train_y ) を 学習データ:検証データ = 7:3 に分割

    from sklearn.model_selection import train_test_split train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=0.3, random_state=34) 結果を確認すると... train_x.shape val_x.shape 確かに 7:3 くらいに分割されていることがわかる 1-2. データの前処理 - バリデーション 337 / 420
  97. 数として Tensor型 を使って自動微分などを行う >>> x = torch.tensor(2.0, requires_grad=True) >>> def

    f(x): ... return x ** 2 + 4 * x + 3 ... >>> y = f(x) >>> y.backward() >>> x.grad tensor(8.) ( の における微分係数 ) ⇨ データをTensor型に直しておく必要あり 1-3. PyTorchに入力できる形に 339 / 420
  98. torch.tensor(data, requires_grad=False) data : 保持するデータ(配列っぽいものならなんでも) リスト,タプル, Numpy配列, スカラ.... requires_grad :

    勾配 (gradient)を保持するかどうかのフラグ デフォルトは False 勾配の計算(自動微分)を行う場合は True にする このあとこいつを微分の計算に使いますよ〜という表明 再掲: Tensor 型のつくりかた 340 / 420
  99.  単にこれで OK! import torch train_x = torch.tensor(train_x, dtype=torch.float32) train_y =

    torch.tensor(train_y, dtype=torch.float32) val_x = torch.tensor(val_x, dtype=torch.float32) val_y = torch.tensor(val_y, dtype=torch.float32) test_x = torch.tensor(test_x, dtype=torch.float32) 1-3. PyTorchに入力できる形に 342 / 420
  100. 1-0. データのダウンロード ⇩ 1-1. データの読み込み ⇩ 1-2. データの前処理 ⇩ 1-2.

    PyTorchに入力できる形に 全体の流れ1 ~モデルに入力するまで 343 / 420
  101. torch.nn.Sequential によるモデルの構築 torch.nn.Sequential を使うと 一直線 • • • のモデルを簡単に定義できる. import

    torch.nn as nn model = nn.Sequential( nn.Linear(30, 32), nn.Sigmoid(), nn.Linear(32, 64), nn.Sigmoid(), nn.Linear(64, 1) ) 2. モデルの構築 346 / 420
  102. 二値分類の場合 ⇨ 最後に シグモイド関数 をかけることで出力を の中に収める. import torch.nn as nn

    model = nn.Sequential( nn.Linear(30, 32), nn.Sigmoid(), nn.Linear(32, 64), nn.Sigmoid(), nn.Linear(64, 1), nn.Sigmoid() # <- ここ重要! ) 2. モデルの構築 ~ 二値分類の場合 347 / 420
  103. import torch.nn as nn model = nn.Sequential( nn.Linear(30, 32), nn.Sigmoid(),

    nn.Linear(32, 64), nn.Sigmoid(), nn.Linear(64, 1), nn.Sigmoid() ) ⇨ すでにこの時点でパラメータの初期化 などは終わっている 引数に層を順番に渡すことで,モデルを構 築してくれる! 「全結合層( ) シグモイ ド関数 全結合層 ( ) シ グモイド関数 全結合層( )」 という MLP の定義 2. モデルの構築 348 / 420
  104. 構築したモデルは関数のように呼び出すことができる import torch dummy_input = torch.rand(1, 30) model(dummy_input) torch.rand(shape) で,形が

    shape のランダムな Tensor が作れる ⇨ モデルに入力して計算できることを確認しておく! (現段階では乱数でパラメータが初期化されたモデルに乱数を入力しているので値に意 味はない) 2. モデルの構築 350 / 420
  105. 現状確認 train_x , train_y , val_x , val_y , test_x

    にデータが Tensor 型のオブジェクトとして格納されている. 3-1. 確率的勾配降下法の準備 357 / 420
  106. TensorDataset に モデルの入力データ ( train_x )と 出力の目標データ ( train_y )

    を渡すことで Dataset のサブクラスである TensorDataset が作れる! from torch.utils.data import TensorDataset # データセットの作成 # 学習データのデータセット train_dataset = TensorDataset(train_x, train_y) # 検証データのデータセット val_dataset = TensorDataset(val_x, val_y) 3-1. 確率的勾配降下法の準備 実際は torch.utils.data.Dataset を継承したクラスを作ることでも Dataset のサブクラスのオブジェクトを作ることができます. この方法だと非常に柔軟な処理が行えるためふつうはこれを使います (今回は簡単のために TensorDataset を使いました) 359 / 420
  107. 1. DataLoaderの作成 ( DataLoader ) Dataset から一部のデータ (ミニバッチ) を取り出して供給してくれるオブジェク ト

    つまり.... 整理: 我々がやらなきゃいけないこと データをいい感じに選んで供給する仕組みを作る をやってくれる 3-1. 確率的勾配降下法の準備 360 / 420
  108. 1. DataLoaderの作成 ( DataLoader ) Dataset からミニバッチを取り出して供給してくれるオブジェクト DataLoader(dataset, batch_size=batch_size, shuffle=shuffle)

    from torch.utils.data import DataLoader batch_size = 32 train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True) val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False) ⇨ これを for文で回すことでデータを取り出すことができる 3-1. 確率的勾配降下法の準備 361 / 420
  109. 1. DataLoaderの作成( DataLoader 型) for inputs, targets in train_dataloader: print('inputs.shape',

    inputs.shape) print('targets.shape', targets.shape) print('-------------') ⇩ inputs.shape torch.Size([32, 30]) targets.shape torch.Size([32, 1]) ------------- inputs.shape torch.Size([32, 30]) targets.shape torch.Size([32, 1]) ... ✔︎ データセットを一回走査するまでループが回ることを確認しよう! 3-1. 確率的勾配降下法の準備 362 / 420
  110. DatasetとDataLoaderの作成 from torch.utils.data import TensorDataset, DataLoader # データセットの作成 train_dataset =

    TensorDataset(train_x, train_y) val_dataset = TensorDataset(val_x, val_y) # データローダの作成 batch_size = 32 train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True) val_dataloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False) 3-1. 確率的勾配降下法の準備 363 / 420
  111. パラメータを微小に変化させても 正解率は変化しない! ⇨ 正解率は, ほとんどの点で微分係数 変わるところも微分不可能 ⇩ 勾配降下法で最適化できない 正解率の微分 右のグラフは,

    適当に作った二値分類 ( ) のタスクをロジスティック回帰 というモデルで解いたときの、パラメータ平面上の正解率をプロットしてみたもので す。これを見ればほとんどのところが微分係数が ( 平坦) で、変わるところも微分 不可 ( 鋭い) ことがわかります。 370 / 420
  112. 確認してほしいこと: 正解 と予測 が近いほど値は 小さくなっている. (  なのでそれぞれの場 合について考えてみるとわかる) 微分可能である なので、損失関数として妥当

    Binary Cross Entropy Loss これもやはり二乗和誤差のときと同様に同様に尤度の最大化として 導出 • • できます. 373 / 420
  113. PyTorch では, torch.nn.BCELoss で使える! import torch criterion = torch.nn.BCELoss() y

    = torch.tensor([0.0, 1.0, 1.0]) pred = torch.tensor([0.1, 0.9, 0.2]) loss = criterion(pred, y) print(loss) # => tensor(0.6067) Binary Cross Entropy Loss 374 / 420
  114. 定義 計算 backward(), 定義 計算 backward(), 定義 計算 backward(), 定義

    定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義 3.2 確率的勾配降下法の実装 377 / 420
  115. # ここから model = nn.Sequential( nn.Linear(30, 32), ... ) #

    ここまでが "定義" dummy_input = torch.rand(1, 30) dummy_target = torch.rand(1, 1) # "計算" pred = model(dummy_input) loss = criterion(pred, dummy_target) # "backward()" loss.backward() 損失に対するパラメータの勾配の計算例 378 / 420
  116. ✔︎ チェックポイント 1. loss に対する勾配を計算している # backward loss.backward() 2. 勾配は

    パラメータ に対して計算される for param in model.parameters(): print(param.grad) ( dummy_input , dummy_target は requires_grad=False なので勾配は計算されない) 3.2 確率的勾配降下法の実装 379 / 420
  117. for epoch in range(epochs): for inputs, targets in train_dataloader: #

    計算 outputs = model(inputs) loss = criterion(outputs, targets) # backward loss.backward() # ----------------------- # .... # ここにパラメータの更新を書く # .... # ----------------------- 3.2 確率的勾配降下法の実装 381 / 420
  118. ( : 完成版ではない) optimizer = optim.SGD(model.parameters(), lr=lr) # 学習ループ for

    epoch in range(epochs): for inputs, targets in train_dataloader: # 勾配の初期化 optimizer.zero_grad() # 計算 outputs = model(inputs) loss = criterion(outputs, targets) # backward loss.backward() # パラメータの更新 optimizer.step() 3.2 確率的勾配降下法の実装 383 / 420
  119. optimizer = optim.SGD(params) のようにすることで params を勾配降下法で更新するオプティマイザを作成できる! たとえば Adam が使いたければ optimizer

    = optim.Adam(params) とするだけでOK! ⇩ 勾配を計算したあとに optimizer.step() を呼ぶと, 各 Tensor に載っている勾配の値を使ってパラメータを更新してくれる 3.2 確率的勾配降下法の実装 384 / 420
  120. from torch import nn model = nn.Sequential( nn.Linear(30, 32), nn.Sigmoid(),

    nn.Linear(32, 64), nn.Sigmoid(), nn.Linear(64, 1), nn.Sigmoid() ) optimizer = torch.optim.SGD(model.parameters(), lr=1e-2) criterion = torch.nn.BCELoss() n_epoch = 100 for epoch in range(n_epoch): running_loss = 0.0 for inputs, targets in train_dataloader: # 前の勾配を消す optimizer.zero_grad() # 計算 outputs = model(inputs) loss = criterion(outputs, targets) # backwardで勾配を計算 loss.backward() # optimizerを使ってパラメータを更新 optimizer.step() running_loss += loss.item() val_loss = 0.0 with torch.no_grad(): for inputs, targets in val_dataloader: outputs = model(inputs) loss = criterion(outputs, targets) val_loss += loss.item() # エポックごとの損失の表示 train_loss = running_loss / len(train_dataloader) val_loss = val_loss / len(val_dataloader) print(f'Epoch {epoch + 1} - Train Loss: {train_loss:.4f} - Val Loss: {val_loss:.10f}') 3.2 確率的勾配降下法の実装 387 / 420
  121. 1行目. for epoch in range(n_epoch) .... データ全体を n_epoch 回まわす 2行目.

    running_loss = 0.0 .... 1エポックごとの訓練データの損失を計算するた めの変数 4行目. for inputs, targets in train_dataloader .... 訓練データを1バッチずつ 取り出す( DataLoader の項を参照してください!) 6行目. optimizer.zero_grad() .... 勾配を初期化する. 二つ前のページのスライド です! 9, 10行目. outputs = ... .... 損失の計算をします. 各行の解説 (for文以降) 388 / 420
  122. 13行目. loss.backward() .... 勾配の計算です.これによって model のパラメータに 損失に対する 勾配が記録されます 16行目. optimizer.step()

    .... optimizer が記録された勾配に基づいてパラメー タを更新します. 18行目. running_loss += loss.item() .... 1バッチ分の損失を running_loss に足 しておきます. 20行目~25行目. 1エポック分の学習が終わったらバリデーションデータでの損失 を計算します. バリデーションデータの内容は学習に影響させないので勾配を計算 する必要がありません.したがって torch.no_grad() の中で計算します. 3.2 確率的勾配降下法の実装 389 / 420
  123. 1. 以上なら異常と予測する. val_pred = model(val_x) > 0.5 2. torch.Tensor から

    numpy.ndarray に変換する val_pred_np = val_pred.numpy().astype(int) val_y_np = val_y.numpy().astype(int) 2. sklearn.metrics の accuracy_score を使って正解率を計算する from sklearn.metrics import accuracy_score accuracy_score(val_y_np, val_pred_np) # => (乞うご期待. これを高くできるように頑張る) 正解率の計算 393 / 420
  124. + オプション  学習曲線を書いておこう 1. 各エポックの損失を記録する配列を作っておく train_losses = [] val_losses =

    [] 1. 先ほどの学習のコードの中に,損失を記録するコードを追加する train_loss = running_loss / len(train_dataloader) val_loss = val_loss / len(val_dataloader) train_losses.append(train_loss) # これが追加された val_losses.append(val_loss) # これが追加された print(f'Epoch {epoch + 1} - Train Loss: {train_loss:.4f} - Val Loss: {val_loss:.10f}') (各 エポックで正解率も計算するとより実験がしやすくなるので実装してみよう) 3. 学習が完了!!! 394 / 420
  125. + オプション  学習曲線を書いておこう matplotlib というパッケージを使うことでグラフが書ける # matplotlib.pyplot を pltという名前でimport import

    matplotlib.pyplot as plt plt.plot(train_losses, label='train') plt.plot(val_losses, label='val') plt.legend() plt.xlabel('epoch') plt.ylabel('loss') plt.show() ⇨ いい感じのプロットが見れる 3. 学習が完了!!! 395 / 420
  126. import csv def write_pred(predictions, filename='submit.csv'): pred = predictions.squeeze().tolist() assert set(pred)

    == set([True, False]) pred_class = ["attack" if x else "normal" for x in pred] sample_submission = pd.read_csv('sample_submission.csv') sample_submission['pred'] = pred_class sample_submission.to_csv('submit.csv', index=False) をコピペ → 5. 順位表への提出 398 / 420
  127. 予測結果 ( True , False からなる Tensor ) pred =

    model(test_x) > 0.5 を作って, write_pred(pred) すると, 5. 順位表への提出 399 / 420
  128. shake Public LB と Private LB の順位が大きく異なる現 象 shake 写真はつい先日終わった

    Learning Agency Lab - Automated Essay Scoring 2.0 とい うコンペの順位表です. こちらのリンク (https://kaggle.com/competitions/learning-agency-lab-automated-essay- scoring-2/leaderboard) から見れます.恐怖. 412 / 420
  129. Public LB に振り回されないために 1. スコアのブレの程度を把握しておく i. テストと同じくらいのサイズのバリデーションデータをとり,そのスコアのブ レを見るなど ii. Public

    Score の上振れを引いても Private Score は上がらないので CV を上 げることに専念 2. バリデーションデータとテストデータの分布の乖離に気を付ける i. たいていのコンペでは参加者同士が CV と LB のスコアを比較するディスカッ ションが立っていがち. これを必ず • • 確認する! ii. 分布の違いの原因を調べて, よりテストデータに近いバリデーションデータを 作る方法を考える (例: adversarial validation) shake の波を乗り切るにはどうするか? 413 / 420
  130. ただ, Public LB も 重要な情報 • • • • •

    (ふつうの) 機械学習の枠組みでは絶対見られないテストスコアの一部が見られる ⇩ 以下のケースでは Public LB も 重要なスコアの指針 1. Public LB 用のデータが学習データと同じ分布で同程度のサイズ 2. 時系列で学習データとテストデータが分割されている i. Public / Private 間はランダムに分割 ← とくに重要な指針になる ii. Public / Private も時系列で分割 Public LB との向き合い方 414 / 420
  131. 1. まず与えられたデータに対して EDA を行い, データの基本的な性質や予測に役立 つ情報を把握する 2. 信頼できるバリデーションの仕組みを構築する 3. 特徴量エンジニアリングを行い,

    学習 4. 提出 5. ディスカッションを参考にしつつ, スコアの信頼性などを確かめる.終盤ならハイ パーパラメータの調整などをしても良いかも. 6. 3 に戻る↩︎ テーブルコンペの全体的な流れ 417 / 420