marp | theme | paginate | math |
---|---|---|---|
true |
honwaka |
true |
mathjax |
traP Kaggle班 @abap34
この資料は 東京工業大学デジタル創作同好会 traP Kaggle班 で 2024年に実施した 「機械学習講習会」の資料です
機械学習に初めて触れる学部一年生のメンバーが
- 基本的な機械学習のアイデアを理解 して
- 最終的にニューラルネットを実際の問題解決に使えるようになること
を目指しています
(講習会自体については https://abap34.github.io/ml-lecture/supplement/preface.html をみてください)
<style scoped> { font-size: 1.0em; line-height: 1.2em; } /* columns は 3つのカラムに分割 */ .columns { display: flex; justify-content: space-between; } /* リストは ▶︎ で始まる */ ul { list-style: none; padding-left: 0; } ul li::before { content: "▶︎"; margin-right: 0.5em; } .box { padding: 0em; padding-left: 1.8em !important; padding-right: 1.8em !important; padding-bottom: 1em !important; margin-bottom: 1em !important; margin: 0em; border: 1px solid #ccc; border-radius: 20px; /* 超薄い水色 */ background-color: #f0f8ff } </style>
[1] 学習
- この講習会について
- 学習とは?
- 損失関数
- トピック: なぜ"二乗"なのか
[2] 勾配降下法
- 関数の最小化
- 勾配降下法
[3] PyTorch と自動微分
- PyTorch の紹介
- Tensor型と自動微分
- トピック: 自動微分のアルゴリズムと実装
[4] ニューラルネットワークの構造
- 複雑さを生むには?
- 「基になる」関数を獲得する
- ニューラルネットワークの基本概念
- トピック: 万能近似性と「深さ」
- DNN の学習の歴史
- 初期化 ?
- 確率的勾配降下法
- さまざまなオプティマイザ
- バリデーションと過学習
[6] PyTorch による実装
- データの前処理
- モデルの構築
- モデルの学習
- モデルの評価
- データ分析コンペの立ち回り
- EDA
- CV と shake
- ハイパーパラメータのチューニング
<style scoped> { font-size: 1.5em; } </style>
- この資料を管理しているレポジトリは 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 までご連絡ください
<style scoped> { font-size: 2em; } </style>
資料の公開にあたって 東京工業大学情報理工学院情報工学系博士後期課程の @YumizSui さん (大上研究室) と 前田航希さん (@Silviase, 岡崎研究室)に 内容について多くの助言をいただきました
この場を借りてお礼申し上げます
2024/06/24 traP Kaggle班
第1回 │ 学習 第2回 │ 勾配降下法 第3回 │ 自動微分 第4回 │ ニューラルネットワークの構造 第5回 │ ニューラルネットワークの学習と評価 第6回 │ PyTorch による実装 第7回 │ 機械学習の応用, データ分析コンペ
機械学習は非常に広大な分野 ⇨ 全7回ではちょっと限界がある
今回の講習会ではとくにディープラーニングについてメインに扱います
- ツールを触るだけで原理は全然やらない
- 原理をやるだけで全然使えない
にならないようにどちらもバランス良くやります
✅ 機械学習の基本的なアイデアを説明できるようになる ✅ ライブラリに頼らず基本的なアルゴリズム, モデルを実装できるようになる ✅ PyTorch を使った基本的なニューラルネットの実装ができるようになる
慣れている人へ → Jupyter Notebook と numpy, matplotlib, scipy, PyTorch あたりのライブラリを使えるようにしておいてください
慣れていない人へ → https://abap34.github.io/ml-lecture/supplement/colab.html をみて Google Colaboratory の使い方を覚えておいてください
-
if文, for文, 関数 など
-
外部パッケージの利用
(そこまで高度なことは求めません ググり力とかの方が大事)
- 基本的な行列の演算や操作 (積,転置など)
- 基本的な微分積分の知識 (偏微分など)
(1年前期の (線形代数) + (微分積分のさわり) くらい)
(ここだけの話機械学習はめちゃくちゃおもしろい)
機械学習の基本的な用語を整理して 「学習」ということばをきちんと説明できるようになる.
- AI(人工知能) 「人間っぽい知能」を実現しようとする分野・あるいは知能そのもの
- 機械学習(Machine Learning, ML) 様々な情報から「学習」をして動作するアルゴリズム 人工知能の一つのかたちと見られることが多い
⬇︎ つまり?
ここでは一つの定義を紹介しましたが, 実際この二つの言葉に明確に定義や合意があるわけではないです. 手法を厳密に分類してもあまり嬉しいことはないと思いますが, とりあえずこの講習会ではこういう形で整理してみることにします.
- 機械学習(Machine Learning, ML) 様々な情報から「学習」をして動作するアルゴリズム
↑ 学習って何?
- 気温↑ → 売れそう
- 気温↓ → 売れなさそう
「アイスの売り上げ」は 「気温」からある程度わかりそう?
< ...来月の売り上げが予想できたらどのくらい牛乳仕入れたらいいかわかって嬉しいな.
一番簡単な方法: 過去の全く同じ状況を参照する
「予測」ってなんだっけ? → 入力を受け取ってそれっぽい出力をすること
⇩
今回は 「入力: 気温」 → 「出力: アイスの売り上げ」
そして ✅ 入力は知ってるものだけとは限らない
このような入力データを受け取り結果を返す
売り上げ =
⇨ 一旦話を簡単にするために
「
のかたちであることにしてみる.
このように各モデルが固有に持ってモデル自身の性質を定める
数を 「パラメータ」という. (
関数
- アイスの売り上げを予測するには, 気温から売り上げを予測する 「関数」を構築するのが必要であった.
- いったん, 今回は関数の形として
$f(x) = ax + b$ (一次関数) に限って関数を決めることにした. - この関数はパラメータとして
$(a, b)$ をもち,$(a, b)$ を変えることで 性質が変わるのがわかった - これからやる仕事は,
「$(a, b)$ をいい感じのものにする」ことで「いい感じの
$f$ を作る」こと
上:
🤔💭 湿度や人口、子供の割合なんかも売り上げとは関係しそうだからこれらも入力に入れたいな。
⇩ グラフが書けない!
案1. 高次元の存在になる 案2. 定量的な指標を考える
良さとは?
⇩
悪くなさ
⇩
悪くなさとは何か?
⇩
データと予測の遠さ
なぜ差を二乗するのか疑問に思った人もいるかもしれません. 全てをここで話すと情報量過多なので一旦置いといてあとで軽く議論します.(末尾の付録)
このモデルの悪くなさを定義する関数を「損失関数」と呼ぶ.
Q. 損失は何の関数? (何を動かして損失を小さくする?)
✅ 各
ものすごく進んだ話: たまに「入力データ」っぽいものに当たるものについても変数とみることもあります. 自分の知っている話だと DeepSDF という三次元形状を表現する NN では latent code と呼ばれる物体固有の表現を表すベクトルも変化させて損失関数を最小化していました.
上:
頑張って計算すると,
⇩
で
- アイスの売り上げを予測するには気温から売り上げを予測する 「関数」を構築するのが必要であった.
- いったん, 今回は関数の形として
$f(x) = ax + b$ (一次関数) に限って関数を決めることにした. - この関数はパラメータとして
$(a, b)$ をもち,$(a, b)$ を変えることで 性質が変わるのがわかった - モデルの「よさ」のめやすとして 「損失関数」を導入した
- パラメータを変えることで損失関数を最小化する過程のことを「学習」と呼ぶ
⇨ 性質がいいから
- 微分可能で導関数も簡単 (絶対値関数は微分不可能な点がある)
- 計算もそんなに大変ではない (百乗誤差などと比べて)
一方で現実の最適化だと微分不可能な点が有限個(何なら可算無限個) あっても何とかなることが多いです.
⇨ 誤差が正規分布
このとき
2024/06/25 traP Kaggle班
- アイスの売り上げを予測するには, 気温から売り上げを予測する 「関数」を構築するのが必要であった.
- いったん, 今回は関数の形として
$f(x) = ax + b$ (一次関数) に限って,関数を決めることにした. - この関数は, パラメータとして
$(a, b)$ をもち,$(a, b)$ を変えることで 性質が変わるのがわかった - モデルの「よさ」のめやすとして, 「損失関数」を導入した
- パラメータを変えることで損失関数を最小化する過程のことを「学習」と呼ぶ
最小化してください.
最小化してください.
- 簡単な数式の操作で解けた!
- 機械的に書くなら
「
$ax^2 + bx + c$ を最小にする$x$ は$x = -\dfrac{b}{2a}$ 」 という公式を使った
プログラムに起こすと...
# ax^2 + bx + c を最小にする x を返す関数.
def solve(a, b, c):
return -b / (2 * a)
最小化してください.
を満たす
いいたかったこと
✅ このレベルの単純な形の関数でも解をよく知っている形で書き表すことは難しい
われわれの目標...
数学の答案で最小値 1 になるところを 1.001と答えたら当然 🙅
第一回では 話を簡単にするために
(特にニューラルネットワーク以降は) 非常に複雑になりうる
複雑そうな式を気分で乗せただけなのであまり意図はありません
⬇︎
例)
⬇︎
すこし負の方向に
✅ 小さくなった
例)
⬇︎
すこし負の方向に
✅ 小さくなった
関数
(
正確にはこれは最急降下法と呼ばれるアルゴリズムで, 「勾配降下法」は勾配を使った最適化手法の総称として用いられることが多いと思います. (そこまで目くじらを立てる人はいないと思いますし, 勾配降下法あるいは勾配法と言われたらたいていの人がこれを思い浮かべると思います.)
マイナーチェンジが大量! (実際に使われるやつは第五回で予定)
- 値が
$-f'(x)$ の方向に更新される - 学習率によって更新幅を制御する
値が
(さっきの説明の通り)
✅ 微分はあくまで「その点の情報」
傾向が成り立つのはその周辺だけ
⬇︎
少しずつ更新していく必要がある
⬇︎
小さな値 学習率
初期値として
✅ その式を (解析的に) 解いた結果が何であるか知らなくても, 導関数さえ求められれば解を探しにいける
最小化してください.
初期値として
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)
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
✅ 勾配降下法があまりうまくいかない関数もある
例)
局所最適解 ... 付近では最小値 (
⇨ なるべく局所最適解に ハマりまくらない ように色々と工夫 (詳しくは第5回)
- Momentum $$ v_{n+1} = \alpha v_n - \eta f'(x_n) \ $$ $$ x_{n+1} = x_n + v_{n+1} $$
- AdaGrad $$ h_{n+1} = h_n + f'(x_n)^2 \ $$ $$ x_{n+1} = x_n - \dfrac{\eta}{\sqrt{h_{n+1}}} f'(x_n) $$ $$ \vdots $$
多変数関数の場合は,微分係数→勾配ベクトル に置き換えればOK
勾配ベクトルは各変数の偏微分係数を並べたものです. 例えば
最小化してください.
$$
- \dfrac{1}{(x^2 + 1)}\log\left(\dfrac{1}{1 + e^{-x}} + 1\right) $$
嫌です.
traP Kaggle班 2024/06/28
- 損失関数の最小化を考える上で, 一般の関数の最小化を考えることにした
- 損失関数の厳密な最小値を求める必要はなく, また損失関数は非常に複雑になりうるので, 広い範囲の関数に対してそこそこ上手くいく方法を考えることにした
- たいていの関数に対して, 導関数を求めることさえできればそれなりに小さい値を探しに行けるようになった
- 逆に, 「導関数」は自分で求める必要がある
最小化してください.
$$
- \dfrac{1}{(x^2 + 1)}\log\left(\dfrac{1}{1 + e^{-x}} + 1\right) $$
第一回では 話を簡単にするために
(特にニューラルネットワーク以降は) 非常に複雑になりうる
✅ 人間が微分を行うのは限界がある ⇨ 計算機にやらせよう!
正確には「自動微分」は, コンピュータに自動で微分を行わせる手法のうち, とくに関数を単純な関数の合成と見て連鎖律を利用して, 陽に導関数を求めることなく微分を行う手法を指します. (より狭義に, back propagationを用いるもののみを指すこともあるようです).
- PyTorchの導入
- PyTorchを使った自動微分
- 自動微分を使った勾配降下法の実装
- 自動微分の理論とアルゴリズム
結論から言うと... 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.)
(
事実:
ニューラルネットワークのさまざまな派生系の
- 基本的な部品
- 部品に対してやる作業
は大体同じ!
例) 新しい車を開発するときも,部品は大体同じ,組み立ても大体同じ
⇩ 毎回同じことをみんながそれぞれやるのは面倒 ⇩ 共通基盤 を提供するソフトウェアの需要がある
- TensorFlow
- (主に) Googleが開発したフレームワーク
- 産業界で人気 (が, 最近はPyTorchに押され気味)
- PyTorch
- (主に) Facebookが開発したフレームワーク
- 研究界で人気 (最近はみんなこれ?)
- Keras
- いろんなフレームワークを使いやすくしたラッパー (おもに TensorFLow)
- とにかくサッと実装できる
- JAX/Flax, Chainer, MXNet, Caffe, Theano, ...
どれがいいの? ⇨ PyTorchを使っておけば間違いない (と, 思います)
- 高速な実行
- 非常に柔軟な記述
- 大きなコミュニティ
- 超充実した周辺ライブラリ
- サンプル実装の充実 (← 重要!!)
大体の有名フレームワークにそこまで致命的な速度差はなく, 記述に関しては好みによるところも多いです.PyTorchの差別化ポイントは, 有名モデルの実装サンプルが大体存在するという点です. 実際に論文を読んで実装するのは骨の折れる作業なので, サンプルが充実していのはとても大きな利点です.
数学の 「数」 に対応するオブジェクトとして,PyTorchでは
を使う
- スカラー: 添字
$0$ 個で値が決まる$\rightarrow$ $0$ 階のテンソル - ベクトル: 添字
$1$ 個で値が決まる$\rightarrow$ $1$ 階のテンソル (v = [1, 2, 3], v[0] = 1
) - 行列: 添字 2 個で値が決まる
$\rightarrow$ $2$ 階のテンソル (M = [[1, 2], [3, 4]], M[0][0] = 1
)
⇩ 例えば
T = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
は T[0][0][0] = 1
)
例) 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 より
data
: 保持するデータ(配列っぽいものならなんでも)- リスト, タプル, NumPy配列, スカラ, ...
requires_grad
: 勾配 (gradient)を保持するかどうかのフラグ- デフォルトは
False
- 勾配の計算(自動微分)を行う場合は
True
にする - このあとこいつを微分の計算に使いますよ〜という表明
- デフォルトは
>>> x = torch.tensor(2.0, requires_grad=True)
Tensor
型のオブジェクトを作成
>>> x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
Tensor
型のオブジェクトを作成
かつては自動微分には Variable
という名前の型が使われていて, (現在は Tensor
型に統合) Tensor
と数学の変数の概念にある程度の対応があることがわかります.
>>> x = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], requires_grad=True)
$\begin{pmatrix} 1.0 & 2.0 & 3.0 \ 4.0 & 5.0 & 6.0 \end{pmatrix}$ という行列を保持するTensor
型のオブジェクトを作成
(requires_grad=True
とすれば, 勾配計算が可能な Tensor
型を作成できる)
これらを勾配計算が可能なTensor
型として表現してください.
$x = 3.0$ $\vec{x} = (3.0, 4.0, 5.0)$ - $X = \begin{pmatrix} 3.0 & 4.0 & 5.0 \ 6.0 & 7.0 & 8.0 \end{pmatrix}$
(このページの内容は, 実際にやらなくてもやり方がわかればOKです)
↓ 問題の続き次のページへ
(実際にやってください)
-
整数
$x = 3$ を勾配計算が可能なTensor
型として表現することを試みてください.また,その結果を確認して説明できるようにしてください.
※ 次のページにヒントあり
1, 2, 3: 講義資料を遡って, torch.tensor
の第一引数と作成されるTensor
型の対応を見比べてみましょう.
4: Pythonのエラーは,
~~たくさん書いてある~
~~Error: {ここにエラーの端的な内容が書いてある}
という形式です."~~Error"というところのすぐ後に書いてある内容を読んでみましょう.
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)
次のページへ
x = torch.tensor(3, requires_grad=True)
としてみると
RuntimeError: Only Tensors of floating point and complex dtype can require gradients
と出力されます. これは「勾配が計算可能なのは浮動小数点数型と複素数型を格納する Tensor
のみである」 という PyTorch の仕様によるエラーです.
Tensor
型は 「数」なので当然各種演算が可能
x = torch.tensor(2.0, requires_grad=True)
例) 四則演算
x + 2
# -> tensor(4., grad_fn=<AddBackward0>)
x * 2
# -> tensor(4., grad_fn=<MulBackward0>)
平方根を取ったり
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>)
ここまでの内容は別にPyTorchを使わなくてもできること PyTorchは 計算と共に勾配の計算ができる!
抑えてほしいポイント:
x = torch.tensor(2.0, requires_grad=True)
足し算をする.
y = x + 2
print(y)
これの出力は,
普通の Pythonの数値では,
x = 2
y = x + 2
print(y) # -> 4
y
がどこから来たのかはわからない (値として
x = torch.tensor(2.0, requires_grad=True)
y = x + 2
y.backward()
すると...
print(x.grad) # -> tensor(1.)
(
- 変数 (
Tensor
型)の定義 - 計算
- backward()
# 1. 変数(`Tensor` 型)の定義
x = torch.tensor(2.0, requires_grad=True)
# 2. 計算
y = x + 2
# 3. backward()
y.backward()
すると x.grad
に計算された勾配が格納される.
定義→計算→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(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義
例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)
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.])
と対応
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.]])
と対応
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.)
に対応
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 に適用される演算のみが問題になる)
- 任意の(勾配が定義できる)計算を
Tensor
型に対して適用すれば常に自動微分可能 - 定義→計算→backward() の流れ
- ベクトル, 行列など任意の
Tensor
型について微分可能. 多変数関数の場合も同様 - 「実際に適用される演算」さえ微分可能ならOK
-
$y = x^2 + 2x + 1$ の$x = 3.0$ における微分係数を求めよ. (https://oj.abap34.com/problems/autograd-practice-1) -
$y = f(x_1, x_2, x_3) = x_1^2 + x_2^2 + x_3^2$ の$x_1 = 1.0, \ x_2 = 2.0, \ x_3 = 3.0$ における勾配を求めよ. (https://oj.abap34.com/problems/autograd-practice-2) -
$f(\boldsymbol{x_1}) = \boldsymbol{x_1}^T \begin{pmatrix} 1 & 2 \ 2 & 1 \ \end{pmatrix} \boldsymbol{x_1}$ の
$\boldsymbol{x}_1 = (1.0, 2.0)^T$ における勾配を求めよ. (https://oj.abap34.com/problems/autograd-practice-3)
x = torch.tensor(3.0, requires_grad=True)
y = x ** 2 + 2 * x + 1
y.backward()
gx = x.grad
print(gx.item()) # -> 8.0
スペースの都合上 import torch
を省略しています
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
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
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)
これまでは,導関数 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_()
最小化してください.
$$
- \dfrac{1}{(x^2 + 1)}\log\left(\dfrac{1}{1 + e^{-x}} + 1\right) $$
👦 < 微分係数を計算してください!
⇩
[いちばん素直な方法]
を、小さい値で近似する 👉
def diff(f, x):
h = 1e-6
return (f(x + h) - f(x)) / h
これでもそれなりに近い値を得られる.
例)
>>> def diff(f, x):
... h = 1e-6
... return (f(x + h) - f(x)) / h
...
>>> diff(lambda x : x**2, 2)
4.0000010006480125 # だいたいあってる
お手軽だけど...
- 誤差が出る
- 勾配ベクトルの計算が非効率
-
本来極限をとるのに小さい
$h$ を とって計算しているので誤差が出る -
分子が極めて近い値同士の引き算に なっていて
$\left( \frac{\color{red}{f(x+h) - f(x)}}{h} \right)$ 桁落ちによって精度が大幅に悪化.
⇩
いい感じに数式の構造をとって計算したい
単に計算過程を表しただけのものを Kantorovich グラフなどと呼び, これに偏導関数などの情報を加えたものを計算グラフと呼ぶような定義もあります. (伊里, 久保田 (1998) に詳しく形式的な定義があります) ただ, 単に計算グラフというだけで計算過程を表現するグラフを指すという用法はかなり普及していて一般的と思われます.そのためここでもそれに従って計算過程を表現するグラフを計算グラフと呼びます.
( 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 が主流です. 「適用される演算のみが問題になる」という節からわかるように, この方法だと制御構文などを気にせず柔軟な計算グラフの構築が可能になるからです. 一方で、静的に計算グラフを作るのはパフォーマンスの最適化の観点からは非常にやりやすいというメリットもあります.
(一旦計算グラフを得たものとして) この構造から導関数を得ることを考えてみる.
[連鎖律]
目標
$ \displaystyle \begin{split} x &= u + v \ y &= u - v \ z &= x \cdot y \end{split} $
のとき,
との対応は
✅ 変数
- 基本的な演算 を用意しておく.
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
- 変数を表すオブジェクトを用意しておき、これの基本的な演算をオーバーライドする.
class Tensor:
def __init__(self, value):
...
def __add__(self, other):
return Add()(self, other)
def __mul__(self, other):
return Mul()(self, other)
詳しくは Julia Tokyo #11 トーク: 「Juliaで歩く自動微分」 をみよう!
PyTorch でもこの方法で勾配を計算している.
traP Kaggle班 2024/07/01
第一回 「学習」 第二回 「勾配降下法」 第三回 「自動微分」
- 予測をするには 「モデル」 を作る必要があった
- モデルのパラメータを決めるためにパラメータの関数である損失関数を導入した
- 複雑になりうる損失関数を最小にするために 「勾配降下法」 を使ってパラメータを探索した
- 自動微分を使うことで, 手で微分をしなくても勾配を得て勾配降下法を適用できるようになった
- 予測をするには 「モデル」を作る必要があった
- モデルのパラメータを決めるために, パラメータの関数である損失関数を導入した
- 損失関数を最小にするパラメータを求めるために勾配降下法を導入した
- 自動微分によって手で微分する必要がなくなった [← 今ココ!]
データさえあれば...誤差を小さくするパラメータを
- 例え複雑な式でも
- 例え自分で導関数を見つけられなくても
探しにいけるようになった! (== 学習ができるようになった!)
ここまでは
⇨ われわれの手法はこの仮定に依存しているか? 🧐
我々の手法 (自動微分と勾配降下法による学習) で満たすべき条件だったのは...
のみ!
⇨ この条件を満たす関数なら どんなものでも 学習できる!
⇨ 直線以外を表現することはできるが
- 二次曲線
- sinカーブ
- 指数カーブ(?)
しか表現できない
これらのパラメータどんなにいじっても
👉
みたいな関数は表現できない
一方, 合成した
非線形でなくてはいけないことに注意してください!
三角関数を 3つ用意
$f_1(x) = \sin(0.5 x)$ $f_2(x) = \cos(0.8 x)$ $f_3(x) = \sin(0.75 x)$
✔︎ それぞれは単純.
一方, 重み付き和をとると
そこそこ複雑になっている 👉
パラメータとして
を考える
⇩
のとき
和をとる 「基になる関数」 にどのような関数を選ぶべきか?
- 三角関数?
- 多項式関数?
- 指数関数?
- もっと別の関数?
これまでの我々のアプローチを思い出すと...
変化させるのが可能なところはパラメータにして, 学習で求める」
[事実1] 最近流行りの機械学習モデルはたいていニューラルネットワークをつかっている
[事実2] ある程度以上複雑なタスクではニューラルネットワークが最も優れた性能を示すことが多い
グラフはILSVRC という画像認識の大会でニューラルネットワークを使ったモデル (AlexNet) が登場し, 圧倒的な精度で優勝した際のスコア. https://medium.com/coinmonks/paper-review-of-alexnet-caffenet-winner-in-ilsvrc-2012-image-classification-b93598314160 から.
基本単位: レイヤー
ニューラルネットワークは 「レイヤー」と呼ばれる基本的な関数の合成によって構成されるモデル
-
入力層 入力を受け取る部分
-
出力層 出力を出力する部分
-
中間層(隠れ層, hidden layer) それ以外
⇩
データの流れは,
PyTorch本体ででデフォルトで定義されているものだけで 160個以上? [1]
[1] torch.nn.Module
のサブクラスの数を数えました.正確な数でないかもしれません.
先に全ての情報を書くと....
パラメータ
各レイヤーが固有にもつ活性化関数
入力として
(これでわかったら苦労しないので、一つずつ見ていきます)
-
$n$ 個の入力を受け取り,$m$ 個出力する -
複雑な関数を表現するアイデア...
- 非線形関数の合成
- 和をとる
をする
パラメータ
各レイヤーが固有にもつ活性化関数
入力として
👆 丁寧に計算の次元を追ってみよう!
演算を
(
-
複雑な関数を表現するアイデア...
- 非線形関数の合成
- 和をとる
をする
出力前に通す 非線形関数
-
シグモイド関数
$\small \sigma(x) = \dfrac{1}{1 + \exp(-x)}$ -
ReLU関数
$\small \mathrm{ReLU}(x) = \max(0, x)$ -
tanh関数
$\small \tanh(x) = \dfrac{\exp(x) - \exp(-x)}{\exp(x) + \exp(-x)}$
など (大量に存在)
✅ 最後に非線形関数を通すことで全結合層が非線形関数になる.
今できたこと
⇩
これを合成している!
非線形関数の合成を繰り返す ⇨ 複雑な関数を表現
⇩
非線形関数の和をとる と
同じことをしている!!
各層の入力
⬇︎
非線形関数の重みつき和
⬇︎
複雑な非線形関数を表現できる! + さらにそれを非線形関数に通す
とくに 全結合層のみからなるニューラルネットワークを 多層パーセプトロン (Multi Layer Perceptron, MLP) という
用語 | 意味 |
---|---|
MLP (Multi Layer Perceptron) | 全結合層のみからなるニューラルネットワーク |
DNN (Deep Neural Network) | 複数の隠れ層を持つニューラルネットワーク |
ANN (Artificial Neural Network) | 人工ニューラルネットワーク.本来の意味のニューラルネットワーク(動物の神経回路) と区別するためこういう名前が使われることがある |
そもそも直線をやめたくなった動機: 👦 < 直線だけしか表現できないのは困る. 👩 < いろいろな関数が表現できるようになりたい.
⇩
どれくらいの関数が表現できるようになったのか?
※ ざっくりとした表現です.
※ ざっくりとした表現です.
- 我々の学習手法は,
$f(x) = ax + b$ というモデルの構造自体に直接依存しているわけではなかった -
$f(x) = ax + b$ というモデルの構造では直線しか表現することができないので, 違う形を考えることにした - 「基になる」簡単な関数の 合成 と 和 を考えることでかなり複雑な関数も表現できることがわかった
- 「基になる」関数の選び方を考える上で, この関数自体もパラメータによって変化させるモデルとしてニューラルネットワークを導入した
- ニューラルネットワークは非常に幅広い関数を表現できることがわかった
- ニューラルネットワークの表現能力は 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.
準備
定義1. シグモイド型関数
を満たす関数を「シグモイド型関数」と呼ぶ.
定理 (Cybenko, 1989)
任意の
平易に書くと,
どんな連続関数も隠れ層が一つのニューラルネットワークで十分に近似できる
とする.
すると,
負なら
とすると
⇨
とすることができる. (例:
さらに
✅ すると 正の大きな数によってステップ関数にしたものと 負の大きな数によってステップ関数にしたものを足し合わせることで 矩形関数を作ることができる!
✅ これさえできればもうOK
連続関数を全て矩形関数の和としてみればよい.
任意の連続関数を近似できるモデルはニューラルネットワークだけ?
⇨ 全然ふつうにNO.
❌「万能近似ができるからニューラルネットワークがよくつかわれる」
+ あくまでそのような
⇩
ニューラルネットワークの優位性を考えるなら,もうすこし議論を進めていく必要がある
<style> .small { font-size: 85%; } </style>
この結果の主張: 十分幅が広い「隠れ層」が一つあれば十分
世の中の主張: たくさんの層がある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). 画像も同論文より
traP Kaggle班 2024/07/03
- 我々の学習手法は,
$f(x) = ax + b$ というモデルの構造自体に直接依存しているわけではなかった -
$f(x) = ax + b$ というモデルの構造では直線しか表現することができないので, 違う形を考えることにした - 「基になる」簡単な関数の 合成 と 和 を考えることでかなり複雑な関数も表現できることがわかった
- 「基になる」関数の選び方を考える上で, この関数自体もパラメータによって変化させるモデルとしてニューラルネットワークを導入した
- ニューラルネットワークは非常に幅広い関数を表現できることがわかった
ニューラルネットワークは非常に多くのパラメータをもつ
(例: 全結合層はそれぞれ
⇩
学習はそれなりに難しいタスク
ニューラルネットワーク研究の歴史を遡ってみると...?
⇩
😯 実は真空管で計算をしている時代からニューラルネット(の原型)が作られて計算されていた
右は真空管を使ったパーセプトロンの計算機を作っている Frank Rosenblatt. 10ニューロン程度のパーセプトロンを作っていたらしい. (画像は https://news.cornell.edu/stories/2019/09/professors-perceptron-paved-way-ai-60-years-too-soon より)
- 1986年ごろ: 多層パーセプトロン → ニューラルネットで全部表現できる!すごい!! → 数学的な研究も進み始める (Hecht-Nielsen, 1987 や Cybenko, 1989 など)
1990年 ~ 2000年代
- ニューラルネットワークを大きくしていくと学習がとたんに難しくなる 😔 (= まともなパラメータを獲得してくれない)
⇩
研究も下火に
[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.
活性化関数の進化 (ReLU) Dropout Batch Normalization オプティマイザの進化 (Adam, RMSprop ...)
⇩
✅ DNN の学習を比較的安定して行えるように
⬇︎
関数
(
勾配降下法...
をニューラルネットワークに適用するための色々な技法
🔲 初期化 (
⇩
🔲 計算 (
のそれぞれをカスタマイズします
勾配降下法...
✅
一般の
⇨ 初期値として普遍的にいい値はない
⇨ NNは構造が固定されているのでいい初期値を考えられる
$$ \begin{cases} W_{i, j} \sim \mathcal{U}\left(-\sqrt{\dfrac{6}{n + m}}, \sqrt{\dfrac{6}{n + m}}\right) \
b_j = 0 \end{cases} $$
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.
- 出力が
$0$ または$1$ に 貼り付く -
$|x|$ が大きいと勾配がほぼ$0$
$$ x_{k+1} = x_k - \eta \color{red} f'(x_k) $$ ⇩
勾配がほとんど
ほとんど同じような「基になる関数」をとっても効率がわるい
出力と勾配両方 について
- 上下に貼り付く (分散大)
- ほとんど同じ値 (分散小)
にならないように
がいい初期値になる
シグモイド関数はよくない性質 (
⇩ ReLU 向けの初期値 (導出は Xavier と一緒)
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.
モデルの構造 (とくに活性化関数) によって適切な初期値のとり方が変わってくる!
例) SIREN [1] という活性化関数に
[1] Sitzmann, Vincent, et al. "Implicit neural representations with periodic activation functions." Advances in neural information processing systems 33 (2020): 7462-7473. 画像も同論文より引用
- 初期値で頑張る
- モデルの中で直してしまう
⇨ 学習の効率化にかなり役立ち 初期化の影響を受けにくくする
Ioffe, Sergey, and Christian Szegedy. "Batch normalization: Accelerating deep network training by reducing internal covariate shift." International conference on machine learning. pmlr, 2015.
✅ 乱数生成をやめると再現性が向上してうれしい.
[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).
- 適切な初期値を選ぶことで学習の安定性を向上させることができる
- Xavierの初期値, Heの初期値などがよく使われる
- 一方, 近年は初期値にそこまで神経質にならなくてもよくなりつつある
- さらに一方で (!?) 特殊なネットワークではそれに適した初期値を使うとよい
☑️ 初期化 (
⇩
🔲 計算 (
🔲
⇩
われわれは自動微分が使えるので
これで
⇩
計算の過程もカスタマイズする!
局所最適解 ... 付近で最小 大域最適解 ... 全体で最小
https://www.telesens.co/loss-landscape-viz/viewer.html で見てみよう!
(
Li, Hao, et al. "Visualizing the loss landscape of neural nets." Advances in neural information processing systems 31 (2018).
画像も同論文より
谷からの脱出方法
データを選ぶときに ランダム性が入る!
⇩
局所最適解にトラップされない
プレーン な勾配降下法の更新式
- 学習率に鋭敏でなく
- 安定して
- 高速に
- 高い性能を得る
ためにいろいろなオプティマイザが提案されている
(PyTorch 本体には13個)
画像は https://pytorch.org/docs/stable/optim.html より (2024年7月3日)
✅
の最小値
を勾配降下法で求めてみる
谷を往復し続けて収束の効率がめちゃくちゃ悪い 😔
アニメーション: https://abap34.github.io/ml-lecture/ch05/img/ch05_gradient_descent.gif
勢い を定義して,前の結果も使って更新する
アニメーション: https://abap34.github.io/ml-lecture/ch05/img/ch05_momentum.gif
momentum で遊べるサイトです. おすすめです https://distill.pub/2017/momentum/
☑️ 初期化 (
⇩
☑️ 計算 (
いよいよ本格的なモデルが作れそうになってきた!
⇨ その前に モデルの「良さ」 についてもう一度考えてみる
⇨ こいつの「良さ」をどう定義するべきか?
学習の際に使ったデータは {(20℃, 300円), (25℃, 350円), (30℃, 400円), (35℃, 450円), (40℃, 500円)}
⇨ さぁこれを使ってアイスの値段を予測するぞ! ⇨ 来るデータは....
なんか来月の予想平均気温30度って気象庁が言ってたな. 来月の売り上げが予想できたらどのくらい牛乳仕入れたらいいかわかって嬉しいな.
⇩
未知のデータ
ほんとうに高めたいもの: 未知のデータへの予測性能
これを新たに良さとしたい!!
学習データ
{ (20℃, 300円), (25℃, 350円), (30℃, 400円), (35℃, 450円), (40℃, 500円) }
-
学習データ { (20℃, 300円), (25℃, 350円), (30℃, 400円) }
-
検証用データ { (35℃, 450円), (40℃, 500円) }
学習データ { (20℃, 300円), (25℃, 350円), (30℃, 400円) }
のみで学習をおこなう
(35℃, 450円), (40℃, 500円)に対して推論を行い,誤差を評価
400円,500円と推論したとすると, 「検証用データに対する」平均二乗誤差は
学習データ: { (20℃, 300円), (25℃, 350円), (30℃, 400円) } のみで学習!
検証用データはパラメータの更新に使わず誤差の計算だけ
⇩ つまり
われわれの真の目標は 未知のデータをよく予測すること
これの計算結果に基づいてモデルを変更することはない. 単に評価するだけ
⇩
計算さえできればいいので,われわれの学習手法で損失関数が満たす必要があった
- 微分可能
などの条件は必要ない!
⇩
もっといろいろなものが使える.
例) 正解率, 絶対誤差 etc....
この検証用データに対して定義される「良さ」を 「評価指標」 という.
つまり 損失関数の値を最小化することで「評価指標を改善する」のが目標.
注意
⇩
逆にいえば 評価指標は直接最適化されない!
⇩
損失関数を最小化することで評価指標が改善するように損失関数を考える.
-
損失関数の値はあくまで「訓練データに対してこれくらいの誤差になるよ」という値
-
ほんとうに興味があるのは, 知らないデータに対してどれくらいうまく予測できるか
-
これの検証のために擬似的に学習に使わない未知のデータを作り, 未知のデータに対する予測の評価をする
バリデーションの手法や切り方についてはいろいろあり, 話すとかなり長くなりますのでここでは割愛します. 例えば Cross Validation や時系列を意識した Validation, テストデータとバリデーションデータの性質を近づけるための手法などもあります。 詳しくは 8月に実施予定の講習会で扱われるはずです!
バリデーションデータは学習データからランダムにとってきたもの. ⇨ 学習データと評価の結果が異なることってあるの? 🤔💭
学習データと検証データに分ける 👉
NN の万能近似性から, 常に損失を
前期の線形代数の知識だけで証明できるので暇な人はやってみてください!
もう少し正確に書くと 「"矛盾のないデータ" (
学習データに対して損失関数を 最小化ヨシ! ✍️
⇩
損失関数は小さくできたが バリデーションデータには全く 当てはまっていない!!
⇨ 学習曲線を見て過学習を見つける
「AI作りました!ちなみにどのくらいの精度かはわからないです笑」
だと実運用はできない
⇩
きちんとバリデーションを行うことで, 未知のデータに対する予測性能を評価することが大切.
逆に, 適切にバリデーションを行なっていないが故の嘘に気をつけよう!!
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
Q. どこが不適切でしょう?
... Randomly selecting 25% of images for validation ....
A. 本来モデルが得るはずがない「未来の情報」が学習時に混入している!
バリデーションはなぜ未知のデータに対する予測性能を疑似的に計算できていたか?
時系列なら 未知の情報に対する精度
1990年のデータが検証用データに入っているなら 1991年以降のデータが学習データに入っていると不当に性能を高く見積もってしまう
Kaggle をはじめとするデータ分析コンペは,「未知の情報」を予測するモデルの精度を競う
⇨ 試行錯誤している手法の「未知の情報を予測する能力」をきちんと評価することが大切! (詳しくは第七回)
bestfitting はこう言っています
- ニューラルネットワークの学習は培われてきたいろいろな工夫があった
- バリデーションを行うことで未知のデータに対しての予測性能を評価することができる.
- バリデーションデータに対して行う評価は学習とは独立した作業なので, 微分可能であったり微分の性質が良い必要はなくいろいろな評価指標を用いることができる.
- 訓練データのみに過剰に適合した状態のことを「過学習」といい, 学習曲線に目を光らせるととでこれに気をつける必要があった
- 適切にバリデーションを行うのは 非常に重要
traP Kaggle班 2024/07/10
- PyTorch を使って実際にある情報を予測するニューラルネットワークを実装します
- データの読み込みからモデルの構築, 学習, 予測までを一通りやってみます
- お題として今日から始めるコンペのデータを使います.
- 1 Sub まで一気に行きます!!
先に、コンペのルールなどの話をします
https://abap34.github.io/ml-lecture/supplement/competetion.pdf
(※ あとからこの資料を読んでいる人は飛ばしても大丈夫です)
機械学習講習会用のオンラインジャッジを作った @abap34
は困っていました.
攻撃はやめてくださいと書いてあるのにひっきりなしに攻撃が仕掛けられるからです.
部員の個人情報とサーバとモラルが心配になった @abap34
は, 飛んでくる通信を機械学習を使って攻撃かを判定することで攻撃を未然に防ぐことにしました.
あなたの仕事はこれを高い精度でおこなえる機械学習モデルを作成することです.
※ 架空の話です. 僕の知る限りジャッジサーバへの攻撃は今のところきていないです.
通信ログから必要そうな情報を抽出したもの (詳細は Data タブから)
- 接続時間
- ログイン失敗回数
- 過去2秒間の接続回数
- 特別なユーザ名 (
root
,admin
guest
とか) でログインしようとしたか?
- train.csv
- 学習に使うデータ
- train_tiny.csv (👈 時間と説明の都合上 今日はこれを使います)
- 学習に使うデータの一部を取り出し,一部を削除
- test.csv
- 予測対象のデータ
- test_tiny.csv (👈 時間と説明の都合上 今日はこれを使います)
- 予測対象のデータの欠損値を埋めて,一部のカラムを削除
- sample_suboldsymbolission.csv
- 予測の提出方式のサンプル (値はでたらめ)
- データの読み込み
- モデルの構築
- モデルの学習
- 新規データに対する予測
- 順位表への提出
✅ セルに以下をコピペして実行
!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
Jupyter Notebook では,先頭に !
をつけることで,シェルコマンドを実行できます.
✅ 左の 📁 > train.csv, test.csv, sample_submission.csv で表が見えるようになっていたら OK!
今回のコンペのデータは 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.
✅ 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")
パスはコンピュータ上のファイルやフォルダへの経路のことです.
今回は train.csv と test.csv がノートブックと同じ階層にあるので, train.csv と test.csv までの経路は,ファイル名をそのまま指定するだけで大丈夫です.
ほかにも たとえば ../train.csv
と指定すると ノートブックの一つ上の階層にある train.csv というファイルを読み込みます.
セルに単に変数をかくと中身を確認できます! (Jupyter Notebook の各セルは最後に評価された値を表示するためです) さっとデバッグするときに便利です. 中身がわからなくなったらとりあえず書いて実行してみましょう.
今まで ⇩
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]
def loss(a):
...
⇩
今回も入力と出力 (の目標) にわけておく
train['カラム名']
で「カラム名」という名前の列を取り出せる 📝
⇩
今回の予測の目標は
train['class']
⇩
train_y = train['class']
⇨ train_y
に攻撃? or 通常? の列が入る🙌
機械学習モデルは 直接的には 数以外は扱えないので数に変換しておく.
train_y = train['class'].map({
'normal': 0,
'attack': 1
})
逆に, モデルに入力するデータは train
から さっきの列 (と id
) を除いたもの!
train.drop(columns=['カラム名'])
を使うと train
から「カラム名」という名前の 列を除いたもの を取り出せる
⇩
今回は train.drop(columns=['id', 'class'])
train_x = train.drop(columns=['id', 'class'])
test_x = test.drop(columns=['id'])
⇨ train_x
にさっきの列と id
を除いたもの, test_x
に id
を除いたものが入る🙌
-
train_x
$\cdots$ モデルに入力するデータ(接続時間,ログイン失敗回数,etc...) -
train_y
$\cdots$ モデルの出力の目標(攻撃? 通常?) -
test_x
$\cdots$ 予測対象のデータ
が入ってる
✅ データをそのままモデルに入れる前に処理をすることで学習の安定性や精度を向上
(極端な例... 平均が
今回は各列に対して「標準化」をします
- 平均
$\mu_1$ のデータの全ての要素から$\mu_2$ を引くと,平均は$\mu_1 - \mu_2$ - 標準偏差
$\sigma_1$ のデータの全ての要素を$\sigma_2$ で割ると,標準偏差は$\sigma_1/\sigma_2$
⇨ 標準化で 平均を0,標準偏差を1 にできる
初期化の際の議論を思い出すとこのようなスケーリングを行うことは自然な発想だと思います. NN の入力の標準化については, LeCun, Yann, et al. "E cient BackProp." Lecture Notes in Computer Science 1524 (1998): 5-50. にもう少し詳しく議論が載っていたので気になる人は読んでみてください.
✅ 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)
scalar.fit
によって引数で渡されたデータの各列ごとの平均と標準偏差が計算され, scalar
に保存されます. そして,scalar.transform
によってデータが実際に標準化されます. 勘がいい人は「test
に対しても train_x
で計算した平均と標準偏差を使って標準化しているけど大丈夫なのか?」と思ったかもしれないですね. 結論から言うとそうなのですが意図しています. ここに理由を書いたら信じられないくらいはみ出てしまったので, 省略します. 興味がある人は「Kaggleで勝つデータ分析の技術」p.124 などを参照してみてください.
train_x
test_x
などを実行してみると,確かに何かしらの変換がされている! ✊
(ついでに結果がテーブルから単なる二次元配列 (np.ndarray
) に変換されてる)
最初のテーブルっぽい情報を持ったまま計算を進めたい場合は,train_x[:] = scaler.transform(train_x)
のようにすると良いです.
ので train_y
もここで中身を取り出して np.ndarray
にしておく.
-
train_y.values
で 中身の値を取り出せる. -
arr.reshape(-1, 1)
でarr
を$N \times 1$ の形に変換できる
train_y = train_y.values.reshape(-1, 1)
np.ndarray
のメソッド reshape
はその名の通り配列の形を変えるメソッドです. そして -1
は「他の次元の要素数から自動的に決定する」という意味です.
例えば, .reshape(-1, 2)
とすると
バリデーションを前処理と呼ぶ人はいないと思いますがここでやっておきます.
train_test_split(train_x, train_y, test_size=0.3, random_state=34)
train_x
,train_y
: 分割するデータtest_size
: テストデータの割合random_state
: 乱数のシード 👈重要!!
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)
乱数に基づく計算がたくさん
⇩
実行するたびに結果が変わって, めちゃくちゃ困る😥
⇩
乱数シードを固定すると, 毎回同じ結果になって 再現性確保
実際はそんな素朴な世の中でもなく, 環境差異であったり, 並列処理をしたとき (とくに GPU が絡んだとき) には単に乱数シードを固定するような見た目のコードを書いても結果が変わりがちで, 困ることが多いです. 対処法もいろいろ考えられているので, 気になる人は jax の乱数生成の仕組みなどを調べてみると面白いかもしれません。
(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 くらいに分割されていることがわかる
✅ PyTorchで扱える形にする
数として 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型に直しておく必要あり
data
: 保持するデータ(配列っぽいものならなんでも)- リスト,タプル, Numpy配列, スカラ....
requires_grad
: 勾配 (gradient)を保持するかどうかのフラグ- デフォルトは
False
- 勾配の計算(自動微分)を行う場合は
True
にする - このあとこいつを微分の計算に使いますよ〜という表明
- デフォルトは
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)
- データの読み込み
- モデルの構築
- モデルの学習
- 新規データに対する予測
- 順位表への提出
✅ 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)
)
⇨ 最後に シグモイド関数 をかけることで出力を
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() # <- ここ重要!
)
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()
)
⇨ すでにこの時点でパラメータの初期化などは終わっている
引数に層を順番に渡すことで,モデルを構築してくれる!
👈 「全結合層(
model.parameters()
または model.state_dict()
で
モデルのパラメータを確認できる
model.state_dict()
各全結合層のパラメータ
✅ 構築したモデルは関数のように呼び出すことができる
import torch
dummy_input = torch.rand(1, 30)
model(dummy_input)
torch.rand(shape)
で,形が shape
のランダムな Tensor
が作れる
⇨ モデルに入力して計算できることを確認しておく!
(現段階では乱数でパラメータが初期化されたモデルに乱数を入力しているので値に意味はない)
- ✅ データの読み込み
- ✅ モデルの構築
- モデルの学習
- 新規データに対する予測
- 順位表への提出
整理: 我々がやらなきゃいけないこと
👉 データをいい感じに選んで供給する仕組みを作る
train_x
, train_y
, val_x
, val_y
, test_x
にデータが
Tensor
型のオブジェクトとして格納されている.
- データセット (データの入出力のペア $\mathcal{D} = {(\boldsymbol{x}i, y_i)}{i=1}^N$) を表すクラス
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)
実際は torch.utils.data.Dataset
を継承したクラスを作ることでも Dataset
のサブクラスのオブジェクトを作ることができます.
この方法だと非常に柔軟な処理が行えるためふつうはこれを使います (今回は簡単のために TensorDataset
を使いました)
Dataset
から一部のデータ (ミニバッチ) を取り出して供給してくれるオブジェクト
つまり....
整理: 我々がやらなきゃいけないこと
👉 データをいい感じに選んで供給する仕組みを作る
をやってくれる
Dataset
からミニバッチを取り出して供給してくれるオブジェクト
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文で回すことでデータを取り出すことができる
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])
...
✔︎ データセットを一回走査するまでループが回ることを確認しよう!
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)
整理: 我々がやらなきゃいけないこと
👉 データをいい感じに選んで供給する仕組みを作る
⇨ あとは学習を実装すればOK!
- 損失関数を設定する
- 勾配の計算を行う
- パラメータの更新を行う
今回の評価指標 👉 正解率!
今までは評価指標もすべて平均二乗和誤差だった
⇩
平均二乗誤差は微分可能なのでこれを 損失関数 として勾配降下法で最適化すれば 評価指標である 平均二乗誤差も最適化できた
正解率は直接最適化できる?
パラメータを微小に変化させても 正解率は変化しない!
⇨ 正解率は,
- ほとんどの点で微分係数
$0$ - 変わるところも微分不可能
⇩ 勾配降下法で最適化できない
右のグラフは, 適当に作った二値分類 (
⇨ こういう分類を解くのに向いている損失関数を使って 間接的に 正解率を上げる.
$$
- \dfrac{1}{N} \sum_{i=1}^{N} \ y_i \log(f(x_i)) + (1 - y_i) \log(1 - f(x_i)) $$
-
正解
$y_i$ と予測$f(x_i)$ が近いほど値は小さくなっている. ($y_i \in {0, 1}$ なのでそれぞれの場合について考えてみるとわかる) -
微分可能である
これもやはり二乗和誤差のときと同様に同様に尤度の最大化として 導出 できます.
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)
☑️ 1. 損失関数を設定する 2. 勾配の計算を行う 3. パラメータの更新を行う
やりかたは....?
定義→計算→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(), 定義 定義→計算→backward(), 定義→計算→backward(), 定義→計算→backward(), 定義
# ここから
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()
loss
に対する勾配を計算している
# backward
loss.backward()
- 勾配は パラメータ に対して計算される
for param in model.parameters():
print(param.grad)
(dummy_input
, dummy_target
はrequires_grad=False
なので勾配は計算されない)
☑️ 1. 損失関数を設定する ☑️ 2. 勾配の計算を行う 3. パラメータの更新を行う
for epoch in range(epochs):
for inputs, targets in train_dataloader:
# 計算
outputs = model(inputs)
loss = criterion(outputs, targets)
# backward
loss.backward()
# -----------------------
# ....
# ここにパラメータの更新を書く
# ....
# -----------------------
これまでは,我々が手動(?)で更新するコードを書いていた
(
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()
✅ optimizer = optim.SGD(params)
のようにすることで
params
を勾配降下法で更新するオプティマイザを作成できる!
たとえば Adam が使いたければ optimizer = optim.Adam(params)
とするだけでOK!
⇩
勾配を計算したあとに optimizer.step()
を呼ぶと,
各 Tensor
に載っている勾配の値を使ってパラメータを更新してくれる
optimizer.step()
で一回パラメータを更新するたびに
optimizer.zero_grad()
で勾配を初期化する必要がある!
(これをしないと前回のbackward
の結果が残っていておかしくなる)
⇩ 次のページ...
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}')
- 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 = ...
.... 損失の計算をします.
-
13行目.
loss.backward()
.... 勾配の計算です.これによってmodel
のパラメータに 損失に対する 勾配が記録されます -
16行目.
optimizer.step()
....optimizer
が記録された勾配に基づいてパラメータを更新します. -
18行目.
running_loss += loss.item()
.... 1バッチ分の損失をrunning_loss
に足しておきます. -
20行目~25行目. 1エポック分の学習が終わったらバリデーションデータでの損失を計算します. バリデーションデータの内容は学習に影響させないので勾配を計算する必要がありません.したがって
torch.no_grad()
の中で計算します.
-
28行目〜30行目. 1エポック分の学習が終わったら, 訓練データと検証データの損失を表示します.
len(train_dataloader)
は訓練データが何個のミニバッチに分割されたかを表す数,len(val_dataloader)
は検証データが何個のミニバッチに分割されたかを表す数です. これで割って平均の値にします. -
32行目. 損失を出力します.
☑️ 1. 損失関数を設定する ☑️ 2. 勾配の計算を行う ☑️ 3. パラメータの更新を行う
バリデーションデータで 今回の評価指標である正解率がどのくらいになっているか計算しておく!
👉 これがテストデータに対する予測精度のめやす.
-
$0.5$ 以上なら異常と予測する.
val_pred = model(val_x) > 0.5
torch.Tensor
からnumpy.ndarray
に変換する
val_pred_np = val_pred.numpy().astype(int)
val_y_np = val_y.numpy().astype(int)
sklearn.metrics
のaccuracy_score
を使って正解率を計算する
from sklearn.metrics import accuracy_score
accuracy_score(val_y_np, val_pred_np) # => (乞うご期待. これを高くできるように頑張る)
- 各エポックの損失を記録する配列を作っておく
train_losses = []
val_losses = []
- 先ほどの学習のコードの中に,損失を記録するコードを追加する
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}')
(各 エポックで正解率も計算するとより実験がしやすくなるので実装してみよう)
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()
⇨ いい感じのプロットが見れる
- データの読み込み
- モデルの構築
- モデルの学習
- 新規データに対する予測
- 順位表への提出
test_x
に予測したい未知のデータが入っている
model(test_x)
⇨ 予測結果が出る
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)
をコピペ →
予測結果 (True
, False
からなる Tensor
)
pred = model(test_x) > 0.5
を作って,
write_pred(pred)
すると,
📂 > submit.csv
ができる!
👉 ダウンロードして, submit から投稿! 順位表に乗ろう!
traP Kaggle班 2024/07/17
- コンペの結果発表 🥳
- データ分析コンペという競技について
- ポエム
⇨ https://abap34.github.io/ml-lecture/supplement/competetion-result.html
Q. 今回のコンペでどんな取り組み方をしましたか?
✅ データ分析コンペにおける勝敗を分けるポイントのひとつ
あたり前に確認すべきこと...
- データはどのくらいあるのか?
- どういう形式なのか?
+ どのような情報が予測に役立つのか?
例) データの分布,欠損値の確認,各変数の組の相関係数 などなど...
ものすごく簡単な例: (abap34.com/ml-lecture/supplement/EDA.html)
Q. Public LeaderBoard に大量の提出を繰り返すとどうなる?
⇨ Public LB でのスコアが上振れる.
Q. するとどうなる?
写真はつい先日終わった Learning Agency Lab - Automated Essay Scoring 2.0 というコンペの順位表です. こちらのリンク (https://kaggle.com/competitions/learning-agency-lab-automated-essay-scoring-2/leaderboard) から見れます.恐怖.
- スコアのブレの程度を把握しておく
- テストと同じくらいのサイズのバリデーションデータをとり,そのスコアのブレを見るなど
- Public Score の上振れを引いても Private Score は上がらないので CV を上げることに専念
- バリデーションデータとテストデータの分布の乖離に気を付ける
- たいていのコンペでは参加者同士が CV と LB のスコアを比較するディスカッションが立っていがち. これを必ず 確認する!
- 分布の違いの原因を調べて, よりテストデータに近いバリデーションデータを作る方法を考える (例: adversarial validation)
ただ, Public LB も 重要な情報
👀 (ふつうの) 機械学習の枠組みでは絶対見られないテストスコアの一部が見られる
⇩ 以下のケースでは Public LB も 重要なスコアの指針
- Public LB 用のデータが学習データと同じ分布で同程度のサイズ
- 時系列で学習データとテストデータが分割されている
- Public / Private 間はランダムに分割 ← とくに重要な指針になる
- Public / Private も時系列で分割
ハイパーパラメータ(学習率, 木の深さ, ... などの学習時の設定) の調整は大事! だけど
✅ ハイパーパラメータの調整は決定的な差別化ポイントになりづらい!
⇨ 調整はそこそこに
- データの理解
- 特徴量エンジニアリング
に時間を費やすのが 🙆
(もちろん, 確実にスコアを上げられる手段なので終盤にはちゃんと調整する)
- まず与えられたデータに対して EDA を行い, データの基本的な性質や予測に役立つ情報を把握する
- 信頼できるバリデーションの仕組みを構築する
- 特徴量エンジニアリングを行い, 学習
- 提出
- ディスカッションを参考にしつつ, スコアの信頼性などを確かめる.終盤ならハイパーパラメータの調整などをしても良いかも.
- 3 に戻る↩︎
- この講習会で扱わなかったこと
この講習会は機械学習の洞窟を全て探検することを目指しているのではなく、一旦ガイド付きで洞窟の最深部まで一気に駆け抜けることで二回目以降の探検をしやすくすることを目指しています。
(前がきより)
✅ 機械学習の世界はめちゃくちゃ広い!
関連する
- 数学
- コンピュータサイエンス
の話題もたくさん (本当にたくさん)
解ける面白い問題もたくさん!
⇨ 必ず興味があるものに遭遇するはず!